线程概述
1.线程的状态
-
NEW:新建的线程,尚未执行
-
Runnable:运行中的线程,正在执行run方法
-
Blocked:阻塞等待,因某些操作被阻塞而挂起
-
Waiting:运行中的线程,因为某些操作在等待中
-
TimeWaiting:超时等待,因为执行sleep()方法正在计时等待
-
Terminated:线程已终止 调用interrupt()方法,可以中断方法
-
blocked和waiting的区别
- block状态是线程阻塞在进入synchronized修饰的方法或代码块时的状态。
- waiting可以通过object.join(),wait()方法达到,表示等待object线程结束,
- block状态是处于wait状态线程的必经之路
-
stop和interrupt的区别?
- interrupt触发异常,线程可以无视这个通知
- stop直接终止线程,并释放锁
线程协调问题
多线程协调原则
当条件不满足时,线程进入等待状态;当条件满足时,线程被唤醒,继续执行任务
- 使用this.wait()进入等待状态,wait()方法必须在当前获取的锁对象上调用,只有在synchronized块中才能调用wait,wait方法在调用后会释放锁
- this.notifyAll(); // 唤醒在this锁等待的线程
- 已唤醒的线程还需要重新获得锁后才能继续执行
ReentrantLock
java.util.concurrent.locks包提供的ReentrantLock用于替代synchronized加锁
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
await()会释放当前锁,进入等待状态;
signal()会唤醒某个等待线程;
signalAll()会唤醒所有等待线程;
唤醒线程从await()返回后需要重新获得锁。
ReadWriteLock
- ReadWriteLock可以提高读取效率:
- ReadWriteLock只允许一个线程写入;
- ReadWriteLock允许多个线程在没有写入时同时读取;
- ReadWriteLock适合读多写少的场景。
StampedLock
- 提供了乐观读锁,可取代ReadWriteLock以进一步提升并发性能
Atomic类
使用java.util.concurrent.atomic提供的原子操作可以简化多线程编程:
- 原子操作实现了无锁的线程安全;
- 适用于计数器,累加器等。
关键字
volatile
用于线程间共享的变量,
告诉虚拟机,每次访问变量时,总是获取主内存的最新值;每次修改变量后,立刻回写到主内存。关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值
synchronized
修饰方法时,锁住的是整个对象
可重入锁
获取锁的时候,获取锁的记录次数+1,退出时-1,直到等于0,才时真正的释放锁
死锁
一个线程获取锁后,再继续获取另外一个锁,这个锁被另一个线程获取过,就会造成死锁,没有任何机制能解除死锁,只能强制结束JVM进程,
避免死锁的方式就是获取锁的顺序要一致
CAS:
- Compare and Set 底层也是java调用汇编指令进行锁的操作
public int incrementAndGet(AtomicInteger var) {
int prev, next;
do {
prev = var.get();
next = prev + 1;
} while ( ! var.compareAndSet(prev, next));
return next;
}
在这个操作中,如果AtomicInteger的当前值是prev,那么就更新为next,返回true。如果AtomicInteger的当前值不是prev,就什么也不干,返回false。通过CAS操作并配合do ... while循环,即使其他线程修改了AtomicInteger的值,最终的结果也是正确的。