欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ReentrantLock等待通知机制Condition介绍

程序员文章站 2022-05-14 09:42:01
Object类中的wait(),notify()和notifyAll()可以实现线程的等待通知模型,同样在ReentrantLock中可以借助Condition来完成这种机制。本篇就简要介绍Condition的工作原理。 先看一下Condition的使用示例: 这段代码的输出为: 等待时间大概为10 ......

  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配合使用。