java.util.concurrent.locks.Condition
介绍:
Condition其实类似Object的Monitor模型(wait,notify,notifyAll),和Object的monitor需要在sychronized里一样,Condition需要和Lock搭档。
Condition可以通过Lock.newCondition()方法获取。某个线程获取了一个Lock,但是需要等待另外的资源才能继续执行,如果等另外的资源而一直占着锁,资源肯定浪费,此时即可用Condition的await方法,将锁暂时释放掉,让其他线程去处理事情。
例如:假设有一个定长的队列,调用take方法时,如果队列的长度为0则会阻塞线程;调用put方法时,如果队列长度等于size,则阻塞线程。当在多线程操作put和take时,通过Condition使put和take所在线程之间协作更有效。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
void await() throws InterruptedException; 类似Object.wait
void awaitUninterruptibly(); 比Object的Monitor有优势地方,加入可中断操作。
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
其中AQS的fifo Node中有一个nextWaiter字段,其实就是为Condition预留的,他实现了一个单链表结构,有head和tail的引用指针。
AQS中ConditionObject.await()流程:
1、增加or初始化 一个waiter链条。
2、释放掉AQS中所有的占有资源(根据state),这个阶段,会将排他锁释放掉,唤醒AQS队列中等待的Head节点线程(唤醒Head后的第一个节点线程很重要)(这个阶段如果Condition之前没有调用lock方法,则抛出IllegalMonitorStateException异常)。
3、挂起自己的线程。等待其他线程唤醒。
/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException
* <li> Save lock state returned by {@link #getState}
* <li> Invoke {@link #release} with
* saved state as argument, throwing
* IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw exception
* </ol>
*/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
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)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
AQS中ConditionObject.signal()流程:
1、找到第一个调用await的Condition节点。
2、重置waiter链表的首位节点。
3、将第一个Condition节点的状态改成初始状态。
4、将第一个Condition节点压入AQS队列。
5、将刚刚压入队列的节点修改状态,改成待唤醒。
当执行signal的线程执行lock.unlock时候,则会唤醒刚刚加入的condition节点。
以下测试代码可以简单看一下condition和FIFO队列的变化。
1、假设有 thread 1 2 main 3个线程。
2、1先获取锁,然后main等待锁,最后是2等待锁。(此时FIFO的状态为: head-->thread main--> thread 2 因为thread1获得了锁,所以没在FIFO队列中)
3、thread1等待3秒后,调用了await方法。thread1初始化了waiter链表结构,头结点为本线程。并且释放了第一个节点:thread main。(此时唤醒了thread main线程,FIFO队列变为:head--> thread 2 )。
4、thread main线程执行await方法,thread main向waiter链表中追加了一个节点。并且释放第一个节点:thread 2(此时唤醒了Thread2线程,FIFO队列变为:head-->null, waiter链表:1-->main)
5、thread2执行signal方法,将waiter的第一个节点压入fifo队列中,并设置状态为:等待唤醒。(此时FIFO队列:head-->thread1 waiter链表:thread main)
6、thread2执行unlock方法,释放FIFO队列等待的线程,thread1被唤醒。
因为只执行了signal一次,所以waiter列表中 还有main线程未释放。如果调用的singalAll方法,则将waiter链表中的所有节点都压入FIFO等待唤醒。
@Test
public void testLock() { //thread main
final ReentrantLock rl = new ReentrantLock();
final Condition newCondition = rl.newCondition();
new Thread(new Runnable() { // thread 1
@Override
public void run() {
rl.lock();
try {
Thread.sleep(3000);
newCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
rl.unlock();
}
}).start();
new Thread(new Runnable() { //thread 2
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
rl.lock();
newCondition.signal();
rl.unlock();
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
rl.lock();
try {
newCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
rl.unlock();
}