多线程-线程通信(wait-notify,await-single,park-unpark)

在多线程场景中,如有些线程需要依赖另外线程的结果而继续执行,如多个线程处理请求,有的处理的快有点慢。快的需要等待慢的线程结果一起提交执行结果。都会涉及到线程间的通信,就是A线程告知B线程处理的结果是怎么样,B线程再执行对应逻辑。通信比较经典的就是采用等待通知模式,当然还有join,volilate等也可作为通信手段。本文重点讲下API层面的通信。

举个例子,三个线程T1,T2,MAIN的场景,T1因为条件不满足进入等待状态,等待其他线程的唤醒(可理解为孩子吃饭没辣条自己关上门在房间里等待辣条)。T2的工作就是唤醒T1线程,但是没满足T1的条件。所以T1还是会进入等待状态(可理解为爸爸为了骗孩子吃饭去敲门叫孩子,说有辣条了,但此时其实没辣条的,所以孩子出门看了一眼,又回去房间里等待了)。MAIN线程也是唤醒线程,并且还满足T1的条件,这时T1就会正常执行。(可理解为妈妈过了一段时间看不下去了,去买了辣条并叫孩子出来吃饭,孩子出来看到有辣条了就开心的吃起来饭

(1)基于Object的wait-notify,此方法需要与syncronized代码块共用。因为底层是采用monitor对象的waitSet去实现。

@Slf4j
class WaitNotify {
    private static boolean flag = false;
    private static Object lock = new Object();
    public static void main(String[] args) {
       new Thread(()->{
            synchronized (lock) {
                while (!flag) {
                    log.info("没辣条不想吃饭,进房间等待");
                    try {
                        lock.wait(); //释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info("哈哈!有辣条吃饭真香");
            }
        },"孩子").start();

        new Thread(()->{
            synchronized (lock) {
                log.info("爸爸给你买辣条了,出来吃饭");
                lock.notify();
            }
        },"爸爸").start();

        SleepUtils.sleep(2);
        synchronized (lock) {
            log.info("妈妈给你买辣条了,出来吃饭");
            flag = true;
            lock.notify();
        }
    }
}

 (2)基于ReentrantLock的await,single。语法比较繁琐,需要先获取到lock,然后要建立一个休息室供线程休息,类似与monitor中的waitSet。最后需要unlock释放锁。

@Slf4j
class AwaitSingle{
    private static boolean flag = false;
    private static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition(); //休息室
    public static void main(String[] args) {
        new Thread(()-> {
            lock.lock();
            try {
                while (!flag) {
                    log.info("没辣条不想吃饭,进房间等待");
                    try {
                        condition.await();//进入休息室等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.info("哈哈!有辣条吃饭真香");
            } finally {
                lock.unlock();
            }
        },"孩子").start();

        new Thread(()->{
            lock.lock();
            try{
                log.info("爸爸给你买辣条了,出来吃饭");
                condition.signal(); //唤醒
            } finally {
                lock.unlock();
            }
        },"爸爸").start();

        SleepUtils.sleep(2);
        lock.lock();
        try {
            log.info("妈妈给你买辣条了,出来吃饭");
            flag = true;
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}

(3)基于LockSupport的park,unpark方法。此方法要注意的是如先调用unpark,则第一次park时不会被阻塞。第二次才会。但并不会unpark调了多次就可以使park失效多次,调用多次unpark也只会失效一次park。

@Slf4j
class Park{
    private static boolean flag = false;
    public static void main(String[] args) {
        Thread t1 = new Thread(()-> {
                while (!flag) {
                    log.info("没辣条不想吃饭,进房间等待");
                    LockSupport.park();
                }
            log.info("哈哈!有辣条吃饭真香");
        },"孩子");
        t1.start();

        new Thread(()->{
            log.info("爸爸给你买辣条了,出来吃饭");
                LockSupport.unpark(t1);
        },"爸爸").start();
        SleepUtils.sleep(2);

        log.info("妈妈给你买辣条了,出来吃饭");
        flag = true;
        LockSupport.unpark(t1);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42740540/article/details/124346905