专注Java教育14年 全国咨询/投诉热线:444-1124-454
赢咖4LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 浅谈多线程CAS

浅谈多线程CAS

更新时间:2021-01-04 08:43:36 来源:赢咖4 浏览1327次

CAS(compare and swap)是解决多线程并行情况下使用锁造成性能损耗的一种机制。多线程CAS操作包含3个操作数,分别是内存位置V、预期原值A和新值B。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。多线程CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

 

这些处理过程有指令集的支持,因此看似读-写-改操作只是一个原子操作,所以不存在线程安全问题。我们看个cas的操作过程伪代码:

                   

int value ;

int compareAndSwap (int oldValue,int newValue){

int old_reg_value =value ;

if (old_reg_value==old_reg_value)

value =newValue;

return old_reg_value ;

 }

                        

当多个线程尝试使用CAS同时更新同一个变量的时候,只有其中一个线程能够更新变量的值。当其他线程失败后,不会像获取锁一样被挂起,而是可以再次尝试,或者不进行任何操作,这种灵活性就大大减少了锁活跃性风险。

 

CAS的特性决定了CAS的功能和作用,我们知道采用锁对共享数据进行处理的话,当多个线程竞争的时候,都需要进行加锁,没有拿到锁的线程会被阻塞,以及唤醒,这些都需要用户态到核心态的转换,这个代价对阻塞线程来说代价还是蛮高的,那cas是采用无锁乐观方式进行竞争,性能上要比锁更高些才是,为何不对锁竞争方式进行替换?

 

要回答这个问题,我们先举个例子。当你开车在上班高峰期的时候,如果通过交通信号灯来控制车流,可以实现更高的吞吐量,而环岛虽然无红绿灯让你等待,但你一圈不一定能绕出你先出去的那个路口,有时候可能得多走几圈,而在低拥堵的时候,环岛则能实现更高的吞吐量,你一次就可以成功,而红路灯反而效率低下了,即便人不多,你依然需要等待。

 

这个例子依然适应锁和cas的比较,在高度竞争的情况下,锁的性能将超过cas的性能,但在中低程度的竞争情况下,cas性能将超过锁的性能。多数情况下,资源竞争一般都不会那么激烈。

我们参考一个ConcurrentLinkedQueue 的源码实现,来看下cas的应用。

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它是个单向链表,每个链接节点都拥有一个当前节点的元素和下一个节点的指针。

 Node< E> {

volatile E item;

volatile Node< E> next;

}

它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部(tail),当我们获取一个元素时,它会返回队列头部(head)的元素。tail节点和head节点方便我们快速定位最后一个和第一个元素。

             

在有两次CAS操作的情况下,如何保证一致性呢?具体分为三种情况如下:

1、如果第一个cas更新成功,第二个失败,那么对了tail会出现不一致的情况。而且即便是都更新成功了,在执行两个cas之间,仍然可能有另外一个线程会访问这个队列,那么如何保证这种多线程情况下不会出错。

 

2、对于第一个问题,即便tail更新失败,上述代码也会循环的找到真正的尾节点,在这里不是强制要求以tail为尾节点,它只是一个靠近尾节点的指针。

 

3、第二种情况,如果线程B抵达时候,发现线程A正在执行更新,那么B线程会通过反复循环来检查队列的状态,直到A完成更新,B线程又拿到了nextNode最新信息,添加新的node,从而使两个线程不会相互干扰。

 

讲了那么多,还是离不开CAS解决多线程并行情况下使用锁造成性能损耗的本质,万变不离其宗,只要我们掌握了这一点学起多线程CAS也就事半功倍了。在本站的多线程教程中还有更多的多线程的各种奇特的机制的详细解析,帮助我们更好地学习多线程知识。

 


提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>