ReentrantLock等待通知机制Condition介绍

2019-02-17 01:51:51来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

  Object类中的wait(),notify()和notifyAll()可以实现线程的等待通知模型,同样在ReentrantLock中可以借助Condition来完成这种机制。本篇就简要介绍Condition的工作原理。

  先看一下Condition的使用示例:

public class LockConditionTest {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    /**
     * 等待方法
     */
    private void startAwait() {
        try {
            lock.lock(); 
            System.out.println("开始等待:" + System.currentTimeMillis());
            condition.await(); 
            System.out.println("等待结束:" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
    
    /**
     * 释放方法
     */
    private void startSignal() {
        try {
            lock.lock();
            System.out.println("开始释放");
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //这里锁被主线程持有,必须释放,让被唤醒的MyThread能够得到锁
            lock.unlock();
        }
    }
    
    
    public static void main(String[] args) throws InterruptedException {
        LockConditionTest test = new LockConditionTest();
        MyThread myThread = new LockConditionTest().new MyThread(test);
        //开始线程。 让线程等待。
        myThread.start();
        //myThread调用condition.await()释放锁,main线程开始执行
        TimeUnit.MILLISECONDS.sleep(1000);
        //主线程唤醒等待线程
        test.startSignal();
    }
    
    
    class MyThread extends Thread{
        private LockConditionTest test;
        
        public MyThread(LockConditionTest test) {
            this.test = test;
        }

        @Override
        public void run() {
            //开始等待,释放锁
            test.startAwait();
        }
    }
    
}

  这段代码的输出为:

开始等待:1550285191899
开始释放
等待结束:1550285192902

  等待时间大概为1000毫秒,符合预期。

  

下面看看Condition的await()方法:

public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();    // 1
            int savedState = fullyRelease(node);  // 2
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);   // 3
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

  主要分为3步:

  1. 把当前线程封装为一个Node节点,把节点加入等待队列
  2. fullyRelease()方法,释放锁
  3. 用LockSupport.park()挂起当前线程

再看看signal()方法:

public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;    // 1
            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);
        }

final boolean transferForSignal(Node node) {
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))    // 2
            return false;

        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);           // 3 
        return true;
    }
  1. 从这段代码可以看到,signal()是唤醒等待队列中的第一个线程
  2. CAS更新节点状态
  3. 唤醒此节点代表的线程

  如果要唤醒全部线程,可以调用signalAll()方法。如果想唤醒部分线程,可以实例化多个Condition配合使用。


原文链接:https://www.cnblogs.com/sunshine-ground-poems/p/10387181.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:SpringBoot从零到上线

下一篇:Redis