欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

ArrayBlockingQueue详解

时间:2023-06-11

ArrayBlockingQueue是由数组实现的阻塞队列,是BlockingQueue的实现类。

特点:

是一个有界的数组,创建的时候传入初始队列大小,

底层采用的是ReentrantLock实现阻塞和用condition条件队列

每次只能有一个线程读写操作,也就是说生产者和消费者公用一把锁,效率其实一般

底层采用数组存储

=================================================================

Node节点,节点存放pre和next节点,还有一个waitStatus状态,还有存放线程的Thread对象。

讲下CLH同步阻塞队列,是一个双向链表,有个head头和tail尾,操作的元素是Node节点

condition阻塞队列,是个单向链表,有个firstWaiter,和lastWaiter,也是用Node节点

================================================================

创建ArrayBlockingQueue:

传入队列初始大小capacity,初始化一个Object数组,初始化一个ReentrantLock,默认非公平锁,初始化两个condition条件队列,notEmpty(处理消费者线程的)和notFull(处理生产者线程的)

生产过程 put过程:

调用put方法,调用ReentrantLock的lock.lockInterruptibly加可中断锁,(ReentrantLock加锁其实底层是一个CLH队列,维护一个volatile int state=0状态,state=0表示没有线程在加锁,state大于0有线程加锁,采用cas操作进行修改state。没抢到锁的会进入CLH队列,然后SupportLock.park进行阻塞,等待抢到锁的SupportLock.unpark释放锁唤醒。大概是这样)

如果获取到锁,判断数组是否满了(count==数组大小),如果没有满,调用enqueue方法写入数组,维护count++,给notEmpty消费者条件队列发送一个信号(notEmpty.signal()),调用doSinal方法,通知存放在condition队列的消费者Node,enq入队到CLH队列,因为必须要到CLH队列,才能获取到ReentrantLock的锁;

如果数组满了,则notFull条件队列会调用await方法阻塞,把当前获取锁的线程node写入到notFull队列,调用addConditionWaiter进行写入condition入队,      condition里node的waitStatus状态是condition(-2),入完队尝试释放锁,调用fullyRelease方法,在调用release方法释放锁,释放锁会唤醒CLH队列中head后面的第一个node,如果这个node还是生产者,会把生产者node写入到notFull条件队列中,判断如果不在CLH同步队列,会把写入condition队列的node进行阻塞park,等待消费者take消费后singal通知,唤醒在notNull等待队列的生产者

写入成功后调用lock.unlock释放锁。

消费过程take过程:

调用take方法,调用ReentrantLock的lock.lockInterruptibly加可中断锁,判断数组数量是否大于0,如果有数据则调用dequeue进行出队消费,并给notFull发送一个signal通知,让在notFull条件等待队列的生产者enq写入CLH同步队列去获取lock锁,然后唤醒CLH队列第一个node节点。

如果数组为空,则让消费者node进行await阻塞,写入到notEmpty条件队列中,这个过程其实和生产者写入notFull条件队列是一样的。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。