volatile不能解决num++这类复合类操作的原子性问题,相比锁机制,使用原子类更精巧。
悲观的解决方案(阻塞同步):
num++操作,1.读取 2. 加一 3. 写入 三步组成的,就是复合类的操作,在并发环境下,如果不做不做何人同步处理,就会有线程安全问题。最直接的处理方式就是加锁。
synchronized(this) {
num++;
}
使用独占锁机制来解决,是一种悲观的并发策略。每次操作数据时都认为别的线程会参与竞争修改,所以直接加锁。同一时刻只能用一个线程持有锁,那其他线程就会阻塞。线程的挂起和恢复会带来很大的性能开销。
乐观的解决策略:
就是每次操作数据的时候,都认为别的线程不会参与竞争修改,也不加锁。如果操作成功了那最好;如果失败了,如中途有别的线程进入并修改了数据,也不会阻塞,一般采用反复尝试的策略。
如AtomicInteger原子类
private volatile int value; //volatile保证可见性
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next)) //CAS保证原子性
return next;
}
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
incrementAndGet的逻辑:
1. 先获取当前的value值
2. 对value加一
3. 第三步时关键,调用comparaAndSet方法来进行原子更新操作,语义是:
先检查当前value是否等于current,如果相等,则意味着value没被其他线程修改过,更新并返回true,如果不相等,compareAndSet则会返回false,然后继续尝试更新。其实就是CAS算法。
CAS + volatile