如何保证原子性

        同一时刻只有一个线程执行,我们称之为互斥,如果我们能够保证对共享变量的修改是互斥的,那么就能保证原子性了。

加锁

        锁是一种通用的技术方案,Java提供的 synchronized 关键字,就是锁的一种实现。

        synchronized 是独占锁/排他锁(就是有你没我的意思),但是注意!sychronized 并不能改变CPU时间片切换的特点,只是当其他线程要访问这个资源时,发现锁还未释放,所以只能在外面等待。 

        synchronized 一定能保证原子性,因为被 synchronized 修饰某段代码后,无论是单核CPU还是多核CPU,只有一个线程能够执行该代码。

        synchronized 也能够保证可见性和有序性。

原子变量

        现在我们已经知道互斥锁可以保证原子性,也知道了如何使用 sychronized 来保证原子性,但 sychronized并不是Java唯一能保证原子性的方案。

        如果你粗略的看一下JUC(java.util.concurrent包),那么你可以很显眼的发现它俩:

        一个是locks包,一个是atomic包,它们可以解决原子性问题。加锁是一种阻塞式方式实现,而原子变量是非阻塞式方式实现。

原子类原理(以AtomicInteger为例)

        原子类的原子性是通过 volatile + CAS 机制实现原子操作的。AtomicInteger 类中的 value 是有 volatile 关键字修饰的,这就保证了 value 的内存可见性,这为后续的 CAS 实现提供了基础。低并发情况下使用AtomicInteger。

CAS机制

CAS

        比较并交换,该算法是硬件对于并发操作的支持。CAS 是乐观锁的一种实现,他采用自旋的思想,是一种轻量级的锁机制。即每次判断预估值和内存中的值是不是相同,如果不相同则说明该内存值已经被其他线程更新过,因此需要拿到该最新值作为预估值,重新判断。而该线程不断的循环判断该内存值是否已被其他线程更新过,这就是自旋的思想。底层是通过 Unsafe 类中的compareAndSwapInt 等方法实现。

CAS 包含了三个操作数:

        ① 内存值 V

        ② 预估值 A (比较时,从内存中再次读到的值)

        ③ B (更新后的值)

        当且仅当预期值 A == V 时, V = B,否则什么都不做。

这种做法的效率高于加锁。当一个线程要进行++操作时,先从内存中取出共享变量,记录一个预估值,将工作内存中++后的值写入主内存前,要判断预估值和主内存中的值是否一致,如果一致,就写入++后的值,如果不一致,说明其他线程修改过,需要重新获取主内存的共享变量,重复之前的操作。

CAS 的缺点

        原子类内部实现使用了不加锁的CAS机制,线程不会被阻塞,不断的自旋会导致CPU的消耗,在并发量大的时候容易导致CPU跑满。

        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值