日期:2020/7/1 桑翔
BlockingQueue阻塞队列
1.通常用数组和链表实现
2.一般而言队列具备FIFO先进先出的特性,当然也有双端队列(Deque)优先级队列
主要操作:入队(EnQueue)与出队(Dequeue)
一.ArrayBlockingQueue
1.定义:由数组支持的有界队列 ,容量大小在创建ArrayBlockingQueue对象时已定义好
//定义队列
BlockingQueue<Ball> blockingQueue = new ArrayBlockingQueue<Ball>(1);
//向队列中放入对象
blockingQueue.put(ball);
//从队列中拿出对象
blockingQueue.take();
1.源码分析
1.1 new ArrayBlockingQueue(1)
非公平锁有自己同步队列CLH,用来记录等待获取独占锁的线程
创建的两组条件分别对应ArrayBlockingQueue的take(取),put(存)两种操作,用来判断队列是否已经满了或者是否为空---------->其实就是创建了2个条件队列,这样就存在了3个队列
1.2 blockingQueue.put(ball);
1.2.1 put操作
1.先要获取 独占锁
2.判断当前队列是否已满(因为该队列是阻塞有界队列)
3.notFull.wait();调用
将线程放入条件队列中排队
释放独占锁
判断是否在同步队列中,不在则阻塞线程
1.2.2 notFull.wait()操作
public final void await()throws InterruptedException {
//如果当前线程被中断则直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//把当前节点加入条件队列(请看下一张代码截图 [1.0])
Node node = addConditionWaiter();
//释放掉已经获取的独占锁资源,并返回释放锁的次数(这里是释放掉所有的锁),因为在put之前是进行过加锁操作的
int savedState = fullyRelease(node);(请看下一张代码截图 [2.0])
int interruptMode =0;
//判断此线程节点在不在同步队列中,如果不在同步队列中则不断挂起
(请看下一张代码截图 [3.0]))
while (!isOnSyncQueue(node)) {
//已经释放了锁,并且已经存在在条件队列中,阻塞线程,跳出while循环
LockSupport.park(this);
//判断此节点是中断的还是可以被正常唤醒的
//1.如果现在不是中断的,即正常被signal唤醒则返回0, 2.如果节点由中断加入同步队列则返回THROW_IE,由signal加入同步队列则返回REINTERRUPT
//会将条件队列中的这个节点转运到同步队列中,因为只有同步队列中的线程才会被唤醒
(请看下一张代码截图 [4.0])
if ((interruptMode = checkInterruptWhileWaiting(node)) !=0)
break;
}
/**
* 走到这里说明节点已经条件满足被加入到了同步队列中或者中断了
* 在处理中断之前首先要做的是从同步队列中成功获取锁资源
*/
if (acquireQueued(node, savedState) && interruptMode !=THROW_IE)
interruptMode =REINTERRUPT;
//走到这里说明已经成功获取到了独占锁,接下来就做些收尾工作
//删除条件队列中被取消的节点
if (node.nextWaiter !=null)// clean up if cancelled
unlinkCancelledWaiters();
//根据不同模式处理中断
if (interruptMode !=0)
reportInterruptAfterWait(interruptMode);
}
创捷节点以后,释放锁