网站诊断书,西安网站排名公司,建筑工程教育网,包装设计报价明细条件队列
同步队列中的线程是为了争抢锁#xff0c;而条件队列中的线程是主动释放锁#xff0c;挂起自己#xff0c;等条件满足时被别的线程唤醒#xff0c;继续工作。 AQS里只有1个同步队列#xff0c;但可以有多个等待队列#xff0c;每个等待队列对应一个ConditionO…条件队列
同步队列中的线程是为了争抢锁而条件队列中的线程是主动释放锁挂起自己等条件满足时被别的线程唤醒继续工作。 AQS里只有1个同步队列但可以有多个等待队列每个等待队列对应一个ConditionObject对象。
public static void main(String[] args) {ReentrantLock lock new ReentrantLock();Condition con1 lock.newCondition();Condition con2 lock.newCondition();
}
// ReentrantLock.java
final ConditionObject newCondition() {return new ConditionObject();
}同步队列的头尾节点是head和tail等待队列的头尾节点是firstWaiter和lastWaiter。 同步队列的头结点是哑节点等待队列没有哑结点。 线程想进入同步队列是没有条件的线程想进入等待队列得先获取锁而且还得是独占锁。 同步队列的节点和等待队列的节点都是Node对象线程离开等待队列后会进入同步队列继续获取锁。
等待 释放锁 await()
public final void await() throws InterruptedException {if (Thread.interrupted()) // 如果当前线程被设置了中断位则1. 清除中断位2. 抛出异常交给用户处理throw new InterruptedException();ConditionNode node new ConditionNode(); // ConditionNode继承自Node类int savedState enableWait(node); // 当前线程释放锁并且组装node对象LockSupport.setCurrentBlocker(this); // for back-compatibility 向后兼容 与LockSupport.park()组合使用让当前线程抛弃锁boolean interrupted false, cancelled false, rejected false;while (!canReacquire(node)) { // canReacquire表示当前节点是否离开等待对了到了同步队列。如果是则跳出循环if (interrupted | Thread.interrupted()) { // 响应中断if (cancelled (node.getAndUnsetStatus(COND) COND) ! 0) // 详见下文单独介绍这个语句含义是如果被设置中断并且当前节点仍然在等待队列那么离开等待队列响应中断break; // else interrupted after signal} else if ((node.status COND) ! 0) { // 当前节点的status仍包含COND即仍处于等待// 这个方法体目的是阻塞当前线程如果能交给线程池提高并发更好如果不能交给线程池则自己执行LockSupport.park()自己挂起try {if (rejected)node.block();elseForkJoinPool.managedBlock(node);} catch (RejectedExecutionException ex) {rejected true;} catch (InterruptedException ie) {interrupted true;}} else // 代表上一个条件为false即当前节点的status不包含COND即已经离开等待队列。但是canReacquire(node)false表示当前节点还未进入同步队列。因此放弃时间片等别的线程先执行。Thread.onSpinWait(); // awoke while enqueuing}// 运行到此当前线程被唤醒并且在同步队列里LockSupport.setCurrentBlocker(null);node.clearStatus(); // 原子性 node.status 0acquire(node, savedState, false, false, false, 0L); // 尝试获取锁// 运行到此当前线程已经重新获取资源if (interrupted) { // 响应中断if (cancelled) { // 当前节点从等待队列被取消没有进入同步队列unlinkCancelledWaiters(node); // 清理当前节点和被设置为CANCALLED的节点throw new InterruptedException();}Thread.currentThread().interrupt();}}private int enableWait(ConditionNode node) {if (isHeldExclusively()) { // 判断当前线程是否是持锁线程node.waiter Thread.currentThread();node.setStatusRelaxed(COND | WAITING); // COND 2,WAITING 1, 因此node的status3ConditionNode last lastWaiter; // 开始将当前node对象放入条件队列if (last null)firstWaiter node;elselast.nextWaiter node;lastWaiter node; // 结束将当前node对象放入条件队列int savedState getState(); // 获取当前线程的stateif (release(savedState)) // 释放锁并且唤醒同步队列的下一个节点return savedState;}// 如果运行到此说明1. 锁空闲无持锁线程 或者2. 当前线程不是持锁线程因此取消当前节点并且抛出异常node.status CANCELLED; // lock not held or inconsistentthrow new IllegalMonitorStateException();
}node.getAndUnsetStatus(COND) COND) ! 0
这个语句的目的是线程安全地将等待节点移出等待队列即避免多个线程重复将同一个节点移出等待队列。线程如何知道节点在不在等待队列通过status。如果status包含COND表示还在等待队列否则不在。
node.getAndUnsetstatus(COND)是node.status node.status ~COND含义是解除COND位。原理是COND2,用二进制表示是0010(从右往左第二位是1)与非运算之后去掉status第二位的1。 (node.getAndUnsetStatus(COND) COND) ! 0true表示当前节点仍然在等待队列。node.getAndUnsetstatus(COND)解除COND位之后返回的是初始status。node.getAndUnsetStatus(COND) COND是初始状态与COND进行与运算如果初始状态含COND那么结果不等于0含义是当前线程运行时当前节点仍然在等待队列。否则当前节点不在等待队列。 为什么不直接判断COND位而是原子性操作呢是为了线程安全。对于某个等待节点多线程环境下只有第一个线程运行(node.getAndUnsetStatus(COND) COND) ! 0为true只有它的getAndUnsetStatus(COND) 返回值包含COND位。之后的线程的getAndUnsetStatus(COND) 返回值都不包含COND位(node.getAndUnsetStatus(COND) COND) ! 0结果永远是false代表当前节点已经不在等待队列。
唤醒 signal与signalAl
public final void signal() {ConditionNode first firstWaiter;if (!isHeldExclusively())throw new IllegalMonitorStateException();if (first ! null)doSignal(first, false);
}public final void signalAll() {ConditionNode first firstWaiter;if (!isHeldExclusively())throw new IllegalMonitorStateException();if (first ! null)doSignal(first, true);
}private void doSignal(ConditionNode first, boolean all) {while (first ! null) {ConditionNode next first.nextWaiter;if ((firstWaiter next) null)lastWaiter null;if ((first.getAndUnsetStatus(COND) COND) ! 0) {enqueue(first);if (!all)break;}first next;}
}signal()方法和signalAll()方法类似都执行参数校验并且交给doSignal()方法执行。 doSignal()方法将节点从等待队列移除放入同步队列。并没有主动唤醒节点线程。 (first.getAndUnsetStatus(COND) COND) ! 0true则当前线程是第一个去除first节点状态COND的线程。因此可以执行enqueue()方法将节点放入同步队列。 如果(first.getAndUnsetStatus(COND) COND) ! 0false则代表当前线程从ConditionNode first firstWaiter语句到本条语句期间first节点的状态已经被别的线程更改了 跳过当前节点尝试更改下一个节点。 signalAll()方法将alltrue代表所有等待节点都会被放入同步队列。