Condition
It is a tool class for multi-thread coordination communication, which allows some threads to wait for a certain condition (condition) together. Only when the condition is met, the thread will be awakened. It is usually used together with (lock) for multiple Lock
threads Coordinate and synchronize access to shared resources. Condition
The interface provides a mechanism similar to Object.wait()
the and Object.notify()
method, but it is more flexible and can control the thread more finely in the process of waiting and notifying. Condition
The interface provides the following methods:
await()
: Make the current thread wait until another thread callssignal()
orsignalAll()
method to wake it up.awaitUninterruptibly()
:await()
Similar to, but it will not return due to thread interruption.signal()
: Wake up a thread waiting on the condition, if there are multiple threads waiting, only wake up one of the threads.signalAll()
: Wake up all threads waiting on this condition.awaitNanos(long nanosTimeout)
: Wait for the specified time, and return if not woken up before the timeout.
Condition
A typical use of is Lock
to create multiple conditions on an object, each condition representing a different wait queue. A thread can wait for a specific condition to acquire a lock at a specific moment. For example, if multiple threads need to access a finite resource pool, you can create a condition for each resource pool and have the threads wait for the corresponding condition. When a resource is available, the corresponding condition can be used to notify the thread waiting for the resource so that it can acquire the resource.
Condition usage example
package org.example;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
private final Queue<Integer> queue = new LinkedList<>();
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void produce() {
int i = 0;
while (true) {
lock.lock();
i++;
// 等待队列不满
while (queue.size() == 3) {
try {
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 生产一个元素
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
queue.add(i);
System.out.println("Produced: " + i);
// 唤醒一个等待的消费者线程
condition.signal();
lock.unlock();
}
}
public void consume() {
while (true) {
// 等待队列不为空
lock.lock();
while (queue.isEmpty()) {
try {
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 消费一个元素
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int num = queue.remove();
System.out.println("Consumed: " + num);
// 唤醒一个等待的生产者线程
condition.signal();
lock.unlock();
}
}
public static void main(String[] args) {
Example example = new Example();
Thread t = new Thread(example::produce);
Thread t1 = new Thread(example::consume);
t.start();
t1.start();
}
}
As shown in the above code, when there are more than 3 items in the queue, the thread of the producer will be blocked and cannot be produced again. At this time, the thread will be blocked, and then the consumer can seize the thread again at this time and start to consume
Condition source code analysis
await source code
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//创建了一个新节点数据结构还是一个链表(如下图所示)
Node node = addConditionWaiter();
//释放当前的锁并唤醒AQS队列中的一个线程
int savedState = fullyRelease(node);
int interruptMode = 0;
//如果当前结点不在AQS队列中则阻塞当前线程
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
// 如果node 的下一个等待者不是null, 则进行清理Condition 队列上的节点
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
The most important code in the middle is while
the loop. The judgment condition here is whether the current node is in the waiting queue. Under normal circumstances, it is definitely not, so at this time the current thread will be blocked waiting to be awakened
Condition node structure diagram
addConditionWaiter
It is very simple here to create a new node, and then maintain the node list, in which the canceled nodes will be cleaned up
fullyRelease
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
就是将AQS的state置为0和exclusiveOwnerThread
置为null,标识当前锁没有人占用然后就是unpark-AQS
中的头节点就是队伍的第一个线程。
signal源码
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
//找到第一个等待的节点
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
这里其实就是去除第一个节点然后再修改一下condition
链表如下图所示
transferForSignal
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
//这里就是将结点的等待状态修改成初始状态0
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).
*/
//这里就是将等待的这个节点添加到AQS队列中
Node p = enq(node);
int ws = p.waitStatus;
//这里将node的上一个结点的等待状态设置成SIGNAL
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
//需要注意的是正常情况下是不在这里unpark的,而是放到AQS队列中等待unpark
LockSupport.unpark(node.thread);
return true;
}
这里很多人都会以为在这里执行了LockSupport.unpark(node.thread);
所以唤醒了线程,其实不是,正常情况下还是要放到AQS队列中等待unpark,比如当前线程unlock了,如果刚好是当前线程可以抢占锁了此时就会unpark
当前线程
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
Condition原理总结
就是在维护两个队列配合使用LockSupport.unpark(node.thread);
和LockSupport.park(node.thread);
两个方法,具体逻辑图如下: