java队列 同步_基于CAS的非阻塞同步队列的实现(一)

基于CAS实现的组件有很多,这里选择非阻塞同步队列的原因是,当有多个线程同时添加元素时,在队列的尾部如何利用cas保证两个操作的原子性是有相应算法的(这两个步骤是尾节点需要指向新增的元素,最后一个节点元素需要指向新增的元素,即新增的元素必须同时有这两个节点指向它)。代码如下:

public class ConcurentQueue {

static class Node {

public Object e;

public AtomicReference nextRef ;

public Node(Object e,Node next) {

super();

this.e = e;

this.nextRef = new AtomicReference(next);

}

}

private Node head = new Node(null,null);

private Node tail = new Node(null,head);

public void add(Object e) {

Node new_node = new Node(e,null);//待插入节点

while(true){

Node curLast = tail.nextRef.get();//当前最后节点

Node next = curLast.nextRef.get();//当前最后节点的next节点

if(next != null){//A 如果当前最后节点的next节点不为空,说明尾节点需要指向next节点

tail.nextRef.compareAndSet(curLast, next);

}else{

if(curLast.nextRef.compareAndSet(null, new_node)){//* B 将当前最后节点的next节点设置为新插入的节点

tail.nextRef.compareAndSet(curLast, new_node);//* C  将尾节点的next节点设置为新插入的节点

return;

}

}

}

}

public Object remove() {

while (true) {

Node next = head.nextRef.get();

if (next == null) {

return null;

} else {

Node nextSetNode = next.nextRef.get();

if (head.nextRef.compareAndSet(next, nextSetNode)) {

return next.e;

}

}

}

}

}

打*的两个地方原本是需要同时成功才能保证正确性的,但根据现在了解的是cas只能保证一个操作的原子性,若要保证两个操作的原子性,初看是不可能的。细心的朋友可以看出这些代码和java并发编程实战中的是几乎一样的,也只有将head节点也作为哑节点这一小小改动,(注意尾节点和最后一个节点是不同的节点),首先队列永远只有两种状态,见书中的原图(不过描述得改成中间态和稳定态,其余的不要看尤其是那个”对立“让人看不懂):

fc734e58c4f36475adeaf438543b1c25.png

其次队列永远只能在最后一个节点后面即稳定态的时候才能插入!

先分析一下代码:

1、当B步骤成功后,还没来得及执行C步骤前,此时队列就处于中间态,其余线程就会执行A步骤将尾节点推到最后一个节点上从而形成稳定态,此时再执行C步骤时尽管失败了但是队列已经稳定;

2、只要队列不稳定时,所有的线程都会设法执行A步骤将队列趋于稳定;

3、C步骤不管是成功还是失败队列都会处于稳定状态;

到这里大家可以去体会一下java并发编程实战中说的:那两个技巧了,说实在的没看懂代码前能看懂那些话么....,还有能想到这种方法确实不容易,想想还是用锁实现比较简单明了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值