非阻塞同步机制

本文详细解读了《JAVA并发编程实践》中的三种非阻塞算法示例,包括非阻塞计数器、非阻塞栈与非阻塞链表的实现原理与操作流程,深入探讨了CAS操作、Treiber算法及其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《JAVA并发编程实践》中提供了3中非阻塞算法的示例

第一个示例,非阻塞计数器。

CAS,比较并交换即Compare-And-Swap。假设CAS有3个操作数--内存位置V、旧的预测值A和新值B,那么它的典型模式为:首先从V中读取值A,由A生成新值B,然后使用CAS原子化地把V的值改成B,并且期间不能有其他线程改变V的值,因为CAS能够发现来自其他线程的干扰。

 

假设有两个线程,同时执行到 @1 ,获得了value的旧值 ;然后同时执行到 @2 根据原则“ 当多个线程试图使用CAS同时更新相同的变量时,其中一个会胜出,并更新变量的值,而其他线程都会失败,重新尝试。 可知,其中一个线程完成了加1操作,而另一个线程失败,重新do循环。让我们细细体会一下这个原则是怎么得到的: 一个线程完成了加1操作后,另一个线程使用CAS时,旧的预期值没有变但内存位置V的值已经更新了,所以此时V的值不等于旧的预期值而导致失败!

第二个示例,非阻塞栈

注: AtomicReference<V>

compareAndSet ( V  expect,  V  update) 

若当前值与期望值expect相等时,原子化地将update值赋给当前值。

假设有两个线程,同时执行到 @1 ,获得了 栈顶元素,并创建了一个新节点指向当前栈顶;然后同时执行到 @2 根据原则“ 当多个线程试图使用CAS同时更新相同的变量时,其中一个会胜出,并更新变量的值,而其他线程都会失败,重新尝试。 可知,其中一个线程完成了插入操作,而另一个线程失败,重新do循环。让我们细细体会一下这个原则是怎么得到的:一个线程完成了插入操作后,另一个线程使用CAS时,旧的预期值没有变但当前栈顶的值已经更新了,所以此时栈顶的值不等于旧的预期值而导致失败!

个示例,非阻塞 链表

首先看  ,为什么要判断 curTail == tail.get() 呢?

必须要有这个判断来保证数据结构总能处于一致状态。如果没有这个判断的话可能出现下面状况。

细细思索一下,如果没有 curTail == tail.get() 这个的话,一个线程将元素3加入队列, 而另一个线程却把next指针的指向了3',元素3就这样被丢弃了,这当然是不行的!也就是我们下文所说的第一个诀窍。

 

插入新的元素涉及到两个指针的更新(两个指针分别为队尾指针和队尾元素的next指针),需要两个操作过程。第一,更新当前队尾元素的next指针,将新元素插入到列表队尾;第二,释放队尾指针,指向新的最末元素。在这两个操作之间,队列处于中间状态,看图示。


图 a 插入前稳定状态


 

图 b 插入期间,队列处于中间状态


图 c 插入完成后,队列再一次回到稳定状态

 

有几个诀窍来完成我们的链表。第一个诀窍是即使在多步更新中,也要确保数据结构总能处于一致状态。也就是说,如果线程B到达时发现线程A正在更新中,B能够知晓操作已经部分完成并且知道不能立即开始自己的更新。那么B就开始等待(通过反复检查队列状态)直到A完成更新,这样两个线程就不会相互影响了。第二个诀窍是,确保如果B到达时发现数据结构正在被A修改,在数据结构中应该有足够多的信息让B去替代A完成更新。如果B“帮助”A完成其操作,那么B可以进行自己的操作,而不用等待A的操作的完成。当A恢复后试图完成其操作,会发现B已经替它完成了。

同时实现两个诀窍的方法是:假设队列处于稳定状态,则尾节点的next域指向null,如果队列处于中间状态,tail.next为非空。所以任何线程都能够通过检查tail.next及时地了解队列状态。进一步而言,如果队列处于中间状态,它能够通过推进队尾指针向前移动一个节点把状态恢复为稳定状态,同时结束任何线程正在插入元素的的操作。

假设有两个线程同时执行到A处,此时tailNext为空,所以两个线程同时执行到C, 根据原则“ 当多个线程试图使用CAS同时更新相同的变量时,其中一个会胜出,并更新变量的值,而其他线程都会失败,重新尝试。 可知,其中一个线程X完成了插入操作,而另一个线程Y失败,重新while循环。当线程Y再次执行到A处时,tailNext != null,从而执行 tail.compareAndSet(curTail, tailNext) ,它将替代A完成‘释放队尾指针,指向新的最末元素’的操作,从而达到稳定状态来完成自己的插入操作。当然,‘释放队尾指针,指向新的最末元素’这个操作更可能是线程A执行的,一般只有在A执行受挫时,才由B替代执行。

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值