LinkedBlockingQueue源码分析

横向比较

   

     LinkedBlockingQueue对应的是LinkedLis和ConcurrentLinkedQueue,LinkedList是一个线程不安全的双向链表结构的队列,ConcurrentLinkedQueue是一个线程安全的单链表结构的队列,ConcurrentLinkedQueue解决了LinkedList线程不安全的问题,而且是通过写时next使用cas和volatile来实现,是一个无锁化操作,极大保证了读写的性能,但由此带来的缺点是读可能数据不一致,而且ConcurrentLinkedQueue是一个无界队列,操作不当的话可能会导致内存溢出。LinkedBlockingQueue主要是解决了ConcurrentLinkedQueue的两个问题:队列的无界和读数据不一致问题,但肯定在性能上就比ConcurrentLinkedQueue差一些。

put("张三")方法

public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
拿到putLock锁,队列现在元素数量count,然后上锁
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
当队列现在元素数量等于最大容量时,使用putLock锁的condition放入await的等待队列中
while (count.get() == capacity) {
notFull.await();
}
直接把新的node挂到last.next上并成为新的last的节点
enqueue(node);
元素数量+1
c = count.getAndIncrement();
元素数量+1后小于最大容量时,使用putLock锁的condition唤醒await等待队列,
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
当放入当前元素之前是空的,说明之前队列中没有元素,此时可能take操作有线程在takeLock的condition的await等待队列中,所以需要尝试signal takeLock锁的condition中await等待队列的线程
if (c == 0)
signalNotEmpty();
}

总结

  1. 在put元素时,首先拿到的putLock重入锁,而且记录元素个数的count也是AtomicInteger,保证了count累加的线程安全性

  2. LinkedBlockingQueue的node节点、head、tail,next等都没有使用volatile修饰,而且挂新节点也没有ConcurrentLinkedQueue那么复杂的自旋和判断,所以这就是锁带来的一个好处(或者说是锁粒度带来的好处,粒度越小可能存在的线程安全问题越多),代码复杂度会大大降低,各种多线程下写失败的情况就不会出现,

  3. 当队列现在元素数量等于最大容量时,使用putLock锁的condition放入await的等待队列中,并会释放putLock,这样就实现了达到容量就阻塞的效果

  4. 元素数量+1后小于最大容量时,此时说明该线程的元素放进队列后也不会达到最大容量,所以使用putLock锁的condition尝试唤醒await等待队列,这么判断的好处是减少signal无效执行的次数和多一个能唤醒putLock锁的condition的awart等待队列的方式,因为大于等于最大容量执行signal方法后,await等待队列中的线程只是挪到了AQS的等待队列,该线程即使获取到锁又会立即进入putLock锁的condition的awart等待队列,因为此时元素数量已经大于最大容量了

  5. 当放入当前元素之前是空的,说明之前队列中没有元素,此时可能take操作有线程在takeLock的condition的await等待队列中,所以需要尝试signal takeLock锁的condition中await等待队列的线程

  6. put和take实际上使用的是两把锁,写和读是互不干扰,这跟ReentrantReadWriteLock就不一样了,ReentrantReadWriteLock是写跟写互斥、写跟读互斥、读跟读不互斥。而LinkedBlockingQueue是写跟写互斥、读跟读互斥、写跟读不互斥。

take()方法

  1. 跟put方法差不多,put方法是在tail挂元素,take是在head出元素,put方法使用了putLock,take方法使用了takeLock

  2. put方法在元素达到最大容量时会使用putLock锁的condition放入await的等待队列,take方法在队列为空时会使用takeLock锁的condition放入await的等待队列

  3. put方法在元素数量+1后小于最大容量会尝试唤醒putLock锁的condition放入await的等待线程,take方法在元素数量大于1时会尝试唤醒takeLock锁的condition放入await的等待线程

  4. put方法在放入当前元素之前是空的时会尝试signal takeLock锁的condition中await等待队列的线程,take方法在拿出当初当前元素之前时满的时会尝试signal putLock锁的condition中await等待队列的线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值