侧边栏壁纸
博主头像
慢行的骑兵博主等级

贪多嚼不烂,欲速则不达

  • 累计撰写 29 篇文章
  • 累计创建 27 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

无锁并发与有锁并发

慢行的骑兵
2021-10-19 / 0 评论 / 0 点赞 / 181 阅读 / 1,902 字

一.CAS效率分析与原子变量

  • 关于synchronize(能不用则不用),是JVM提供的锁,其影响的范围是跟CPU有关。会造成阻塞现象,阻塞现象会造成线程上下文的切换(实际上是做了很多事情);
    • 1.当前线程需要执行的指令,以及它的程序计数器,内部的一些数据是需要保存的;

1.线程上下文切换

  • 本质:CPU切换前把当前任务的状态保存下来,以便下次切换回这个任务时可以再次加载这个任务的状态,然后加载下一任务的状态并执行。任务的状态保存及再加载, 这段过程就叫做上下文切换(可以复习一下操作系统的内容)。
  • 每个线程都有一个程序计数器(记录要执行的下一条指令),一组寄存器(保存当前线程的工作变量),堆栈(记录执行历史,其中每一帧保存了一个已经调用但未返回的过程);
  • 程序计数器是一个专用的寄存器,用于表明指令序列中 CPU 正在执行的位置,存的值为正在执行的指令的位置或者下一个将要被执行的指令的位置;
  • 上下文切换会导致额外的开销,常常表现为高并发执行时速度会慢串行,因此减少上下文切换次数便可以提高多线程程序的运行效率;
  • 直接消耗:指的是CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉;
  • 间接消耗:指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小;

2.CAS

  • 是一种策略,为了保证主内存中的数据在被多个线程赋值的使用,是一个准确的;

  • 为了达到这个目的,其采取的方案是:

    • 把旧值保留,拿旧值与主内存比对,如果不对,重读再加载;
  • synchronize与cas性能比较的区分点不在于锁的状态,而是数量,Android开发人员跟后端的开发人员并发环境是不一样的,Android的环境中,CAS用的比较多;

  • CAS必须和volatile配合使用:在保证线程安全性的前提下,要保证可见性;

  • 运用CAS的理论,能完成跟锁一样的效果;

    • 没有加锁,但是一直在检测,通过了就放掉(todo 见code);
    • synchronize是阻塞掉,直接放掉CPU资源,下一次过来用的时候,恢复寄存器数据;
  • 单纯的CAS理论

    • 单纯的CAS理论只是为了完成一次比较确认值的同步
    • 与代码块的同步并没有关系

3.CAS理论应用下的锁实现原理

  • 利用volatile变量与CAS理论保证在一定时间段内变量结果的一致性;
  • 同步对于线程进行阻塞;
  • 问题:CAS这种无锁模式去进行安全处理会比有所快吗?
    • 1.线程数量不错(最好和CPU核数对应,其是最优的方案)
    • 2.如果上来就是100000线程,那CAS效率绝对慢;
    • 3.总结:单核或者线程数量过高的情况下,使用有锁的;
  • CAS无锁状态下与synchronize有锁状态下的本质区别
无锁情况下,即使重试失败,线程始终在高速运行,没有停歇,而synchronized会让线程在没有获得锁的时候,发生上下文切换,进入阻塞;
比喻:高速上飙车,当前自己开200码,正常的高速运行,但是一旦发生上下文切换,需要减速停车,换路,在加速,代价相对高昂;
无锁状态下,因为线程需要保持运行,则需要额外CPU的支持,CPU在这里就是高速公路,没路我们走不下去,一开始没有加锁,不会有阻塞,但是没有时间片,会导致上下文切换,所以CAS需要有多核CPU对于其进行支撑;

4.CAS效率分析

  • 结合CAS与volatile实现无锁并发情况的适用场景:多核CPU场景下,且线程数少;
  • CAS基于乐观锁思想,最乐观结果,不怕别的线程来修改共享变量,改了也没事,我在重试;
  • synchronize基于悲观锁思想:最悲观结果,得放着其他线程来修改共享变量,我上锁,你们都别改,我改了解开你们才有机会;
  • CAS体现的是无锁并发,无阻塞并发因为没有synchronized,线程不会陷入阻塞,这是效率提升的因素之一;
  • 如果竞争激烈,重试必然发生频繁,效率会下降;
  • 最好结果为线程数不超过CPU核心数;

5.原子变量

  • 本质上是一组工具,位置在atomic包下;
  • 原子(的概念):只有一个变量能引起线程安全的问题称之为原子;
  • 对于单个属性进行处理,提供了Atomic工具;
  • 所有的原子操作实际上是在底层进行了CAS的死循环比较,只有达成目的,就退出;
  • 在实际并发中会出现的问题场景,其中绝大部分是对于某个属性的数据安全的处理,Atomic实现使用的是CAS理论,在线程数不多、核数合适的情况下,当前方案的速度优于synchronize;

6.原子引用与ABA问题

  • 在多线程对于原子变量操作时,会发生将数据变更回去的现象,CAS在判断时会造成概念上的认知错误,但是实际上对业务结果是不变的;
  • 但是实际业务运用过程中可能会需要知道整个运行过程值是否改变;
  • 通过AtomicStampedReference:追溯版本号
  • 通过AtomicMarkableReference:得到是否更改结果

7.原子累加器

  • 知晓其原理即可(业务场景略)

  • LongAdder:专门用来做数据累加、递减;

  • LongAdder与Atomic比较

    • 性能提升的原因很简单,就是有竞争时,设置多个累加单元,然后最后结果汇总,它这样的累加操作不同的cell变量,因此减少了Cas重试失败,从而提高性能;
  • 伪共享问题

  • 原子累加器了解的比较浅(后续再根据情况选择是否深入)

8.总结

  • 于并发处理,从业务角度我们看做为两块:
    • 1.原子变量操作
    • 2.业务代码块的并发
  • 并发手段现在接触的是两种:
    • 1.加锁并发:synchronize(悲观体现)
    • 2.无锁并发:CAS应用实现(乐观体现)
0

评论区