Condition介绍

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

    }

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值