多线程之CAS与synchronized的比较
业务场景:需要实现一个支持并发的计数功能
1、计数功能的基本实现是:
public class Increment{
private int count = 0;
public void add(){ count++; }
}
2、以上实现在并发环境下是不安全的,故修改方案1是加锁synchronized:
public class Increment{
private int count = 0;
public synchronized void add(){ count++; }
}
//悲观锁,加锁后只能有一个线程你执行++操作,其他线程需要等待
//不会出现count计数不准确的问题,线程安全
3、但是以上实现,会让线程串行化,排队等待获取锁、加锁、处理数据、释放锁,并发下显得不合理
修改方案2是使用Java并发包concurrent下的Atomic原子类
public class Increment{
private AtomicInteger count = new AtomicInteger();
public synchronized void add(){
count.incrementAndGet();
}
}
//多个线程可以并发的执行AtomicInteger的incrementAndGet()方法,把count的值累加1并返回累加后最新的值
//Atomic原子类底层用的是无锁化的CAS机制,保证多线程修改一个数值的安全性
4、实现原理:
(1)每个线程都会先获取当前的值,接着走一个原子的CAS操作,原子的意思就是这个CAS操作一定是自己完整执行完的,不会被别人打断;
(2)在CAS操作里,比较一下,现在的值跟刚才我获取到的那个值,是否相等,是则说明没有人改过这个值,那么将它设置成累加1之后的一个值;
(3)同理,若有人在执行CAS时,发现自己之前获取的值与当前的值不一样,说明有其他人修改了值,导致CAS失败,失败之后进入一个循环,再次获取值,再执行CAS操作。
5、CAS的问题:
每次去比较的时候,都发现值被别人改了,就会进入无限重复的循环。大量线程高并发时相当于空循环,自旋转,性能和效率都不是特别好。
Java8的新类LongAdder,尝试使用分段CAS以及自动分段迁移的方式来提升多线程高并发执行CAS操作的性能。核心思想是热点分离,类似concurrentHashMap