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(); }