20171204_ConcurrentLinkedQueue源码的一些问题

今天在看ConcurrentLinkedQueue的1.8源码,结合网上的一些博客,发现有一些地方有坑,不是太好理解,就写下来总结一下吧。
首先,ConcurrentLinkedQueue是一个并发容器,它的并发性是通过CAS来实现的,这是实现非阻塞并发算法的基础。然后,head/tail 并非总是指向队列的头 / 尾节点,也就是说允许队列处于不一致状态这个特性把入队 / 出队时,原本需要一起原子化执行的两个步骤分离开来,从而缩小了入队 / 出队时需要原子化更新值的范围到唯一变量,这是非阻塞算法得以实现的关键。另外,下面的两点,很重要,这是理解算法的关键:

head的不变性和可变性条件

不变性:
1. 所有未删除节点,都能从head通过调用succ()方法遍历可达。
2. head不能为null。
3. head节点的next域不能引用到自身。
可变性:
1. head节点的item域可能为null,也可能不为null。
2. 允许tail滞后(lag behind)于head,也就是说:从head开始遍历队列,不一定能到达tail。

tail的不变性和可变性条件

不变性:
1. 通过tail调用succ()方法,最后节点总是可达的。
2. tail不能为null。
可变性:
1. tail节点的item域可能为null,也可能不为 null。
2. 允许tail滞后于head,也就是说:从head开始遍历队列,不一定能到达tail。
3. tail节点的next域可以引用到自身。

下面来看入队操作:

public boolean offer(E e) 
{
     checkNotNull(e);
     final Node<E> newNode = new Node<E>(e);
     for (Node<E> t = tail, p = t;;)   
     {
         Node<E> q = p.next;
         if (q == null)             
         {
             if (p.casNext(null, newNode)) 
             {
                     if (p != t)        //1
                     casTail(t, newNode);   
                 return true;
             }
         }
         else if (p == q)   //2
             p = (t != (t = tail)) ? t : head; 
         else   //3
             p = (p != t && t != (t = tail)) ? t : q;
     }
 }

在这我捡几个疑惑说:
1. if (p != t) 在单线程中是不需要考虑这个问题的,这个if判断一直为假。那么,在多线程中,这个判断什么时候为真呢?答案是在下面的else if,p可能会赋值为head,是不是会感到奇怪,在2中我会解释。p != t,说明此时tail没有更新,我们用casTail方法进行更新。在这里,casTail即使失败也没有关系,因为这说明有其他线程对tail更新了。
2. if (p == q) 什么情况下会出现,上面我们在tail的可变性上说了,tail.next可以指向自己,这表明tail现在所在指向的结点已被删除(从head遍历无法到达tail),那么就要从head开始遍历到所有的未删除结点。这就是我们要将p赋值为head的原因。
3. 剩下这种情况就是tail不是指向最后一个节点的时候,此时我们将p更新为q。

再来看出队操作:

public E poll()
{
      restartFromHead:
      for (;;) 
      {
          for (Node<E> h = head, p = h, q;;) 
          {
              E item = p.item;
              if (item != null && p.casItem(item, null))    
              {
                  if (p != h)       //1
                      updateHead(h, ((q = p.next) != null) ? q : p);
                  return item;
              }
              else if ((q = p.next) == null) //2
              {
                  updateHead(h, p);
                  return null;
              }
              else if (p == q)  //3
                  continue restartFromHead;
              else  //4
                  p = q;
          }
      }
  }

看明白上面offer的几个疑点,相信这就不难解释了
1. 这和offer的1很相似,这时我们更新head节点。
2. head的item为null,而它next节点为null的时候,将head指向p这个伪节点,返回null。
3. p == q这个条件有点奇怪,应该是在4时候将q赋值给p,循环后另一个线程将q这个节点给删掉了,此时进入3
4. 在结点出队失败后可以保证下次尝试出队时p不为空(之前q = p.next != null才有可能跳到这一步)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值