显示条件Condition await() signal()(gold_axe)

老马说编程

和 Object 的 wait()/notify()对比

Object.wait/notify有的, Condition 都有

wait/notify与synchronized配合使用 , 显式条件与显式锁配合使用。

aCondition.await()--------aObject.wait()
aCondition.signal()--------aObject.notify()

Object.wait/notify 的缺陷
  • 欺骗性唤醒(Java缺陷, 没通知就唤醒)
  • wait(限时) 是void 不会返回是等到了还是超时了
  • 只能把等待的都唤醒, 不能分组唤醒

所以 一律用Condition 的 await/signal 这对,上下文切换比较少

基本使用:

如下 await() signal() 必须在lock.lock()后使用

public class WaitThread extends Thread {

    private volatile boolean fire = false;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    @Override
    public void run() {
        try {
            lock.lock();//加锁---------------------------------------------------------------------------------
            try {
                while (!fire) {//1.保护条件
                    condition.await();//2.等待
                }
            //3.目标动作
            } finally {
                lock.unlock();//解锁-------------------------------------------------------------------------
            }
            System.out.println("fired");
        } catch (InterruptedException e) {
            Thread.interrupted();
        }
    }

注意:

  • while (!fire) 保护条件必须这样循环 而不是 if , 再检查一遍
  • 1.保护条件 2.等待 3.目标动作 --------三者必须在一个临界区

通知唤醒↓:

    public void fire() {
        lock.lock();//---------------------------------------------------------------------------------------
        try {

            //紧挨着lock.unlock();最后才写唤起其他线程
            this.fire = true;//1.条件满足
            condition.signal();//2.叫醒
        } finally {
            lock.unlock();//--------------------------------------------------------------------------------
        }
    }
    
}

注意:

  • 通知 必须在临界区之内, 只有自己得到锁了 才能通知其他线程
  • 通知尽量在临界区最后写: 因为通知后 本线程结束了, 锁释放了, 被唤醒的才能继续
public static void main(String[] args) throws InterruptedException {
        WaitThread waitThread = new WaitThread();
        waitThread.start();
        Thread.sleep(1000);
        
        waitThread.fire();
    }

超时控制

    // 计算等待的最后期限
    final Date deadline = new Date(System.currentTimeMillis() + timeOut);
    // 是否继续等待
    boolean continueToWait = true;
    lock.lock();
    try {
      while (!ready) {
        
        // 等待未超时,继续等待
        if (!continueToWait) {
          // 等待超时退出
          return;
        }
        continueToWait = condition.awaitUntil(deadline);
      }// while循环结束

       // 执行目标动作
    } finally {
      lock.unlock();
    }
  }
  • continueToWait = condition.awaitUntil(deadline); 会返回是不是超时了
  • 有2个出口, 一个是while(条件满足) , 一个是 if (!continueToWait)超时了也要结束

分组唤醒

比synchronized好的地方还有,可以分组唤醒
比如生产者消费者,用synchronized的话, 有生产了一个 只能把 [生产者] 和 [消费者] 都唤醒,而不是只把 [消费者] 唤醒

显示条件Condition await() signal()(gold_axe)_第1张图片

避免了不必要的唤醒。Java并发包中的类ArrayBlockingQueue就采用了类似的方式实现。

原理

ReentrantLock 里面有等待队列
也有条件队列
signal满足条件把条件队列移到等待队列最后
还是原来先在等的线程优先

显示条件Condition await() signal()(gold_axe)_第2张图片

↓ do while 循环把条件队列里面按顺序转移到阻塞队列
显示条件Condition await() signal()(gold_axe)_第3张图片

↓把节点连入双向链表 并且前一个节点要标记 有后继要唤醒
显示条件Condition await() signal()(gold_axe)_第4张图片

你可能感兴趣的:(显示条件Condition await() signal()(gold_axe))