通过Condition实现wait与指定线程notify

  Object 的 wait / notify / notifyAll 几个方法可以实现对线程的挂起和唤醒。ReentrantLock 也可以,不过要借助我们的 Condition 条件监视器,而且 Object 唤醒部分县城是随机唤醒,Condition 使得线程唤醒是可控的,可以指定唤醒部分线程。

首先我们用 ReentrantLock 和 Condition 实现 Object 中对应的方法

public class Myservice {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await(){
        try{
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"await begin");
            condition.await();
            System.out.println(Thread.currentThread().getName()+"await end");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signal(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"  signal begin");
            condition.signal();
            System.out.println(Thread.currentThread().getName()+"  signal end");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signalAll(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"signalAll begin");
            condition.signalAll();
            System.out.println(Thread.currentThread().getName()+"signalAll end");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

在来一个测试线程类:

public class TestConditionThread extends Thread{
    private Myservice service ;
    public TestConditionThread(Myservice service){
        this.service = service;
    }
    @Override
    public void run() {
        service.await();
    }
}

在启动线程并且测试:

public class CliTest {
    public static void main(String[] args) throws InterruptedException {
        Myservice myservice = new Myservice();
        TestConditionThread t = new TestConditionThread(myservice);
        t.setName("A:");
        t.start();
        TestConditionThread t1 = new TestConditionThread(myservice);
        t1.setName("B:");
        t1.start();
        TestConditionThread t2 = new TestConditionThread(myservice);
        t2.setName("C:");
        t2.start();
        Thread.sleep(1000);
        myservice.signal();
        myservice.signal();
        myservice.signal();
    }
}

这里写图片描述

  我们可以发现,被唤醒的线程是有顺序的,并不是随机唤醒的。这是因为这个阻塞队列在AQS实现的是一个FIFO队列。先被阻塞的,先被唤醒。
  signalAll 就是顺序的把队列中的线程全部唤醒,自己可以试下。
  

唤醒部分线程实现

如果想要单独唤醒部分线程,这时候就有必要使用多个Condition对象了,也就是Condition对象可以唤醒部分指定线程,有助于提升程序效率。可以先对线程分组,然后再唤醒组中的指定线程。

Demo

public class NotifyOtherThreadService {
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();

    public void awaitA(){
        try{
            lock.lock();
            System.out.println(Thread.currentThread().getName()+": await A begin");
            conditionA.await();
            System.out.println(Thread.currentThread().getName()+": await A end");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try{
            lock.lock();
            System.out.println(Thread.currentThread().getName()+": await B begin");
            conditionB.await();
            System.out.println(Thread.currentThread().getName()+": await B end");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 唤醒A组中的所有线程
     */
    public void signalAll_A(){
       try {
           lock.lock();
           System.out.println(Thread.currentThread().getName()+": signalAll A begin");
           conditionA.signalAll();
           System.out.println(Thread.currentThread().getName()+": signalAll A end");
       }finally {
           lock.unlock();
       }
    }
    /**
     * 唤醒B组中的所有线程
     */
    public void signalAll_B(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+": signalAll B begin");
            conditionB.signalAll();
            System.out.println(Thread.currentThread().getName()+": signalAll B end");
        }finally {
            lock.unlock();
        }
    }
}

测试类:

public class NotiFyOtherThreadTest {
    public static void main(String[] args) throws InterruptedException {
        NotifyOtherThreadService service = new NotifyOtherThreadService();
        new Thread(()->{
            service.awaitA();
        },"A").start();
        new Thread(()->{
            service.awaitB();
        },"B").start();
        Thread.sleep(1000);
        service.signalAll_B();
    }
}

结果

这里写图片描述

根据结果可以知道,B组线程被唤醒,而A组线程还在阻塞之中。

详细源码分析需要明天的Condition与AQS了,不过笔者已经分析过一波ReentrantLock的源码,有兴趣可以戳https://blog.csdn.net/WeiJiFeng_/article/details/81390935

猜你喜欢

转载自blog.csdn.net/WeiJiFeng_/article/details/81709849