Condition
在多线程中经常用到。 为什么会有Condition 呢?
Condition接口中的方法: 可以很清楚的看到它要做的事情,就是 等待 和 唤醒。

类比 synchronized 与 wait 和 notify的搭配使用。
wait 和 notify 必须要在 同步块中使用,否则会报 异常。
wait 和 notify 协作,来实现线程之间的通信。 等待 通知 机制。
wait 和 notify 只能在同步块中进行使用。也就是必须先获取了synchronized加的锁,才能进行wait和notify操作,而且wait和notify的调用者,是加锁的对象。否则报IllegalMonitorStateException .
执行完 notify 方法后,不会立刻释放锁,这是虽然被wait等待的线程被唤醒了,但是由于获取不到锁,又会被阻塞,直到 notify 所在的同步块执行完后,释放掉占有的锁,被唤醒后的线程重新去抢占到锁后,才会继续向下执行。 wait方法执行后会立即释放掉占有的锁,而notify方法不会。
线程执行wait方法后,如果线程的中断状态为true, 那么就会抛出中断异常。(怎么实现的???)
执行sleep方法后,不会放弃占有的锁,并且如果sleep的线程的中断状态为true,那么就会抛出中断异常。
ReentrantLock用到的Condition 是 ConditionObject 类。
Condition中主要用到的就是 await 和signal ,线程调用 await 方法后会释放锁,并将当前线程添加到等待队列,等待唤醒。这对阻塞队列会有什么影响呢?如果当前线程的结点是存在于阻塞队列中,那么还要靠当前线程所在结点去唤醒下一个结点。(执行unpark方法,底层通过 调用unpark, 并传入下一个Node结点中的thread)。如果当前线程释放掉锁后,就进入到等待队列,睡眠当前线程,那么就没法调用unlock方法了,阻塞队列中的后面的线程也无法获取到锁了。如果一直不被唤醒? 那怎么办???
Condition 内部有一个队列(等待队列),由Node结点组成的等待队列,这个队列和lock的阻塞队列要区别开。
ConditionObject 中的变量: 等待队列中的每个Node结点中存储着等待的线程。
public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** First node of condition queue. */ private transient Node firstWaiter; // /** Last node of condition queue. */ private transient Node lastWaiter;
/** Mode meaning to reinterrupt on exit from wait */
private static final int REINTERRUPT = 1;
/** Mode meaning to throw InterruptedException on exit from wait */
private static final int THROW_IE = -1;
await 方法:
public final void await() throws InterruptedException { if (Thread.interrupted()) // 如果线程的中断状态是 true, 直接抛出异常 throw new InterruptedException(); Node node = addConditionWaiter(); // 新创建结点,并加入到CONDITION的链表中 int savedState = fullyRelease(node); // 释放掉的锁的state的值 int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this); // 也是调用的 LockSupport中的park方法来阻塞线程,与lock的在底层是一样的, 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); }
1) ConditionObject中的 addConditionWaiter 方法:
private Node addConditionWaiter() { Node t = lastWaiter; // If lastWaiter is cancelled, clean out. if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; // 将不符合CONDITION 状态的结点从链中剔除后的尾节点 } Node node = new Node(Thread.currentThread(), Node.CONDITION); // 新创建结点 if (t == null) // 说明原先不存在链 firstWaiter = node; // 新创建的结点作为头结点 else // 如果存在链 t.nextWaiter = node; // 新创建的结点作为尾节点的下一个结点 lastWaiter = node; return node; }
ConditionObject中的 unlinkCancelledWaiters 方法:
private void unlinkCancelledWaiters() { // 检查链中不是CONDITION状态的结点,将其从链中剔除 Node t = firstWaiter; Node trail = null; while (t != null) { // 如果链是存在的 Node next = t.nextWaiter; // 下一个结点 if (t.waitStatus != Node.CONDITION) { t.nextWaiter = null; if (trail == null) firstWaiter = next; // 第一个结点失效,下一个结点作为头节点 else trail.nextWaiter = next; if (next == null) lastWaiter = trail; } else trail = t; t = next; } }
2) AQS的 fullyRelease 方法
final int fullyRelease(Node node) { boolean failed = true; try { int savedState = getState(); // 同步状态 在ReentrantLock中表示的是 是否有线程占有锁 以及可重入锁的进入次数 if (release(savedState)) { // 释放掉当前线程占有的锁 (由于是可重入锁,锁的state的值可能不止是1,这里是完全释放)。 failed = false; return savedState; } else { throw new IllegalMonitorStateException(); // 没有成功释放掉锁 就抛出异常 感觉好像不会出错啊,只要是成功占有了锁
// 这里就一定会成功释放掉锁, } } finally { if (failed) node.waitStatus = Node.CANCELLED; } }
AQS 的 release 方法:
public final boolean release(int arg) { if (tryRelease(arg)) { // 如果成功释放掉锁 Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); // 唤醒被lock 阻塞的后面的线程 return true; } return false; }
tryRelease 方法在 AQS 中没有被实现,在 ReentrantLock 中得到了实现。
protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) // 如果释放锁的线程 不是当前锁的占有者 throw new IllegalMonitorStateException(); // 抛异常 boolean free = false; if (c == 0) { // 如果成功 设置了state的值 free = true; setExclusiveOwnerThread(null); // 将排他锁的占有者的设为 null,表示此时没人占有锁 } setState(c); return free; }
3) AQS 的 isOnSyncQueue 方法
final boolean isOnSyncQueue(Node node) { if (node.waitStatus == Node.CONDITION || node.prev == null) return false; if (node.next != null) // If has successor, it must be on queue return true; /* * node.prev can be non-null, but not yet on queue because * the CAS to place it on queue can fail. So we have to * traverse from tail to make sure it actually made it. It * will always be near the tail in calls to this method, and * unless the CAS failed (which is unlikely), it will be * there, so we hardly ever traverse much. */ return findNodeFromTail(node); }
4) AQS 的 acquireQueued 方法
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 死循环 final Node p = node.predecessor(); // p 的前一个结点 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
ConditionObject 的 signal 方法:
public final void signal() { if (!isHeldExclusively()) // 判断执行signal方法的线程是不是 当前锁的占有者 throw new IllegalMonitorStateException(); // 如果不是,抛异常 Node first = firstWaiter; if (first != null) // 如果存在 等待队列 doSignal(first); // }
1)AQS中没有对 isHeldExclusively的具体实现,ReentrantLock中进行了方法的重写:
protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); }
2)ConditionObject中对 doSignal 方法的实现。
private void doSignal(Node first) { do { if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); }
AQS中的 transferForSignal方法:
final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; /* * Splice onto queue and try to set waitStatus of predecessor to * indicate that thread is (probably) waiting. If cancelled or * attempt to set waitStatus fails, wake up to resync (in which * case the waitStatus can be transiently and harmlessly wrong). */ Node p = enq(node); // 为什么还要入队呢? int ws = p.waitStatus; if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); // 唤醒线程 return true; }
参考文档:
Condition : https://blog.csdn.net/a1439775520/article/details/98471610
浙公网安备 33010602011771号