累加器例子:add10K() 这个方法不是线程安全的,问题就出在变量 count 的可见性和 count+=1 的原子性上
1.可见性问题可以用 volatile 来解决, 2.原子性问题我一直都是采用的互斥锁。
public class Test { long count = 0; void add10K() { int idx = 0; while(idx++ < 10000) { count += 1; } } }
对于简单的原子性问题,还有一种无锁方案:
1.将原来的 long 型变量 count 替换为了原子类 AtomicLong, 2.原来的 count +=1 替换成了 count.getAndIncrement(), 3.仅需要这两处简单的改动就能使 add10K() 方法变成线程安全的
public class Test { AtomicLong count = new AtomicLong(0); void add10K() { int idx = 0; while(idx++ < 10000) { count.getAndIncrement(); } } }
无锁方案优势:性能
1.互斥锁方案为了保证互斥性, 1) 需要执行加锁、解锁操作,而加锁、解锁操作本身就消耗性能; 2) 同时拿不到锁的线程还会进入阻塞状态, 3) 进而触发线程切换,线程切换对性能的消耗也很大。 2.相比之下,无锁方案 1) 完全没有加锁、解锁的性能消耗, 2)同时还能保证互斥性
getAndIncrement() 方法内部就是基于 CAS 实现的: