First, what is the Condition?
Previous multi-thread communication using the Object class provides wait
and notify
methods, Condition
have similar wait
, notify
functions, and provides an interrupt when the thread whether to make the appropriate treatment. Condition
Based on the underlying FIFO queue, it must be combined with Lock
a lock used together, and Condition
examples of the Lock
created. Finally, summed up, Condition
it is a multi-threaded communications tools, shows a state of multi-thread in participation data competition, is responsible for suspend and resume work on the threads of a multithreaded environment.
Below posted a interception from "Art concurrent programming in Java," the comparison chart:
Two, Condition source code analysis
1, Condition architecture and its internal structure is like?
Condition
It is an interface, jdk provides Condition
an implementation class ConditionObject
, ConditionObject
is AQS
in the inner class, because Condition
the operation requires access to relevant locks, and AQS
is the basis to achieve synchronization lock. Condition
Providing several operations following method:
void await() throws InterruptedException
When this method is called, the thread is suspended until the other thread wakes up. Note that when the suspended thread is interrupted, will throw InterruptedException
an exception.
void awaitUninterruptibly()
This approach with the above await()
method is similar, except that this method does not handle the situation the thread interruption.
long awaitNanos(long nanosTimeout) throws InterruptedException
It represents the longest wait for a specified time, unless interrupted midway or early wake-up call, return value = nanosTimeout- have time to wait.
void awaitUninterruptibly()
This approach with the above await()
method is similar, except that this method does not handle the situation the thread interruption.
boolean await(long time, TimeUnit unit) throws InterruptedException
The same awaitNanos()
, except that you can specify the time units.
boolean awaitUntil(Date deadline) throws InterruptedException
It represents a thread hang, knowing some point wake up the thread.
void signal()
as well asvoid signalAll()
Wake-up operation.
So the question is, how to obtain Condition
the object of it? View Lock
interface, which declares a newCondition()
method that returns an Condition
object, such as Lock
an implementation of the interface ReentrantLock
in provides a realization of the method, but the essence is to create ConditionObject
objects.
Source as follows:
public Condition newCondition() {
return sync.newCondition();
}
复制代码
Which sync
is inherited from AQS
the synchronizer.
final ConditionObject newCondition() {
return new ConditionObject();
}
复制代码
About AQS
, you can refer to the following article:
----- Java Concurrency the AQS (abstract queue synchronizer)
2, the internal await implementation details Condition
Below is await()
a method source code:
public final void await() throws InterruptedException {
// 1、线程如果中断,那么抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 2、将当前线程包装成为一个Node节点,加入FIFO队列中
Node node = addConditionWaiter();
// 3、释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
// 4、判断节点是否在同步队列(注意非Condition队列)中,如果没有,则挂起当前线程,因为该线程尚未具备数据竞争资格
while (!isOnSyncQueue(node)) {
// 5、挂起线程
LockSupport.park(this);
// 6、中断直接返回
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 7、参与数据竞争(非中断时执行)
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 清理条件队列中状态为cancelled的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
复制代码
We can see from the above code, because await
itself does not support interrupts, so if the current thread is interrupted, then the first step direct throw. The second step of the current thread packed into a node Node, was added to the Condition
condition of the queue, Node
and AQS
use the same type of view AQS
understood, each Node
with a current Thread
associated.
static final class Node {
volatile Thread thread;
}
复制代码
The third step is then performed to release the lock, here it comes to why Condition
you must get the problem when using the lock, below is fullyRelease()
the source code:
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;
}
}
复制代码
Step 4-6 is continuous cycle until the node appear in the synchronization queue, pay attention to the synchronization queue is not a queue condition, belonging to AQS
a part of Condition
, then this is how to determine whether the while loop in the synchronization queue it? Actually very simple, is that when the node into non-state CONDITION
on it, such as call signal()
, but please note that LockSupport.park(this);
after this code is executing the blocking code, huh, later analysis signal
will be mentioned. Source as follows:
final boolean isOnSyncQueue(Node node) {
// 判断节点的状态是不是CONDITION,CONDITION表示该节点正在处于等待某个条件,此时就应该park挂起线程
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node);
}
复制代码
Step 7 is also well understood, since the conditions for the current thread to wait has been awakened, then the data directly involved in the competition to get away, step 8 to clean out some of the state cancelled
of the node, due to a thread break or timeout, state of the node will be markedcancelled
3, the signal internal implementation details Condition
public final void signal() {
// 1、必须获得锁
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);
}
复制代码
Requirements Call of signal condition of the current thread has acquired the lock, which will make the wait queue head node that is the longest waiting time to synchronize the nodes into the queue, the queue after synchronization and moved to have the opportunity to make the waiting thread wake up, return (this) method, that is, from await methods LockSupport.park (this method is referred to as obstructive live current code above), which have a chance to make thread calls await method successfully quit
Third, the summary
Use lock combination Condition
can solve the problem of producers and consumers, need to pay attention is to use Condition
must first acquire the lock, otherwise the error, that is, generally in the following manner to call Condition
.
lock.lock();
condition.await();
condition.signal();
lock.unlock();
复制代码