LinkedBlockingQueue源码研读

上次被问到实现线程安全的队列,想了下LinkedBlockingQueue源码读的一知半解,所以再来一次

断点1设置了可中断的锁,这个也符合put操作可中断的特性

断点3 c==0容易误解,注意这个c是getAndIncrement的结果,所以c是目前的size-1,所以当c==0时代表size为1,即可以唤醒阻塞在notEmpty的线程

然后就是断点2和断点3下面的signalNotEmpty的设计,这里困惑了好久,为什么只在size==1时唤醒一次阻塞在notEmpty的线程,然后由put操作唤醒阻塞在notFull上的线程?

这个思路是这样的,只有队列从空到非空的那次操作会唤醒notEmpty,让一个线程执行take操作,之后如果take成功且队列还是非空,则由执行take操作的线程唤醒notEmpty上的其他线程

这样做的原因是,如果不在take中唤醒notEmpty而是每次put都试图唤醒notEmpty,则需要每次put操作都请求takeLock,这样的话分别锁住take和put的意义就没有了

另外如果那样做,则会出现很多put操作不断唤醒多个线程,但是同一时间又只能有一个线程竞争到锁执行take操作,这样会出现不必要的竞争。如果使用原来的设计,由于take操作只有一个线程在执行,那么他执行完之后再唤醒另一个线程,这样就规避了很多竞争;只有当take完之后队列空了才不去唤醒别的线程,而是等待一个put操作去调用signalNotEmpty

不得不说,真的妙!!!!

还有一个小的地方

 

这个地方215行h.next=h是看似没有意义的一行,注释说有助于GC,非常疑惑

OpenJDK的LinkedBlockingQueue实现:节点类和GC [重复] 说是问过作者,作者说可以减少浮动垃圾,想了一下垃圾清理的过程,想了半天也没想到怎么减少。如果执行到这里时并发标记已经标记过h为可达,那h肯定就成为浮动垃圾了

可能还有额外的优化,我能猜测道的是,并发标记时就发现这个h所对应的实例只被head和h显示引用过,然后这一步h.next=h又可以确保的h.next不是外部引用了(自环了),所以分析下面的时候可以通过分析head不再指向此实例且h随着栈弹出也会消失,可以分析出可以回收。等一下 ,那也不对啊,h.next=null不也一样么。搞不清楚

另外,记录一些关于LinkedList的感想

 

 

即使使用两个锁分别锁住入队和出队操作,LinkedList也是不安全的,有两个原因:

  1. 当线程A执行到断点处,还未执行赋值语句时被调度,此时线程B开始removeFirst操作,会发生:l.next=newNode还未执行,但first(头节点)已经向后移动了一个,如果last和first是同一个节点,就会导致first成为空,再执行l.next=newNode将last设置为newNode
  2. 那么BlockingLinkedList怎么避免这一点的呢,首先在赋值这一步BlockingLinkedList使用了last=last.next=node也就是先执行last.next=node再将last后移,这就解决了这一点。另外BlockingLinkedList在空的时候是有一个空节点的,此时空节点既是head又是tail,而LinkedList在空时last和first都是null

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值