美文网首页
四、【Java并发】线程间通信之wait,notify,noti

四、【Java并发】线程间通信之wait,notify,noti

作者: deve_雨轩 | 来源:发表于2019-06-10 10:12 被阅读0次

    线程间通信

    在多线程编程当中,很多时候我们需要一个线程修改了某个值之后,而另外一个线程能够感知到变化从而进行响应的操作。比较笨的方法呢就是不断轮询判断,如果条件满足进行响应的操作,反之则等待若干时间后再次轮询判断。就如下代码所示:

    while(条件判断){ 
        Thread.sleep(5000);
    }
    doSomeThing();
    

    上面这段伪代码看似能够实现所需的功能,但是却有着很严重的问题:

    • 难以确保及时性。在休眠的时候,确实不会消耗处理器资源,但是通过睡眠机制,就不能及时的发现条件已经变化。
    • 难以降低开销。如果降低睡眠的时间,比如1毫秒,这样确实能迅速的发现条件变化,但是却可能消耗更多的处理器资源,造成不必要的浪费。

    以上两个问题,看似矛盾难以取舍。但是Java通过等待/通知机制就能很好的解决问题并实现所需的功能。

    初识 wait & notify

    等待/通知相关方法是任意Java对象都具备的,因为这些方法是被定义在java.lang.Object对象上的,方法描述如下

    方法名称 描述
    notify() 通知一个在对象上等待的线程,使其从wait()方法返回,返回的前提是该线程重新获取到了对象的锁
    notifyAll() 通知所有等待在改对象上的线程
    wait() 调用该方法的线程进入阻塞状态并且会释放对象的锁,只有等待其他线程通知或被终端才会返回,
    wait(long) 等待指定一段时间,时间一到就返回
    wait(long,int) 等待指定一段时间,对于超时时间更加细粒度的控制,可以达到纳秒

    示例代码:

    public class WaitNotifyTest {
        static boolean flag = true;
        static Object  lock = new Object();
    
        public static void main(String[] args) throws Exception {
            Thread waitThread = new Thread(new Runnable() {
                @Override
                public void run() {
                 // 加锁
                    synchronized (lock) {
                        // 当条件不满足时,继续wait,同时释放了lock的锁
                        while (flag) {
                            try {
                                System.out.println(Thread.currentThread().getName() + " wait 开始");
                                lock.wait();
                                System.out.println(Thread.currentThread().getName() + " wait 结束");
                            } catch (InterruptedException e) {
                            }
                        }
                        // 条件满足时,完成工作
                        System.out.println(Thread.currentThread().getName() + " 开始工作");
                    }
                }
            }, "WaitThread");
            waitThread.start();
            
            TimeUnit.SECONDS.sleep(1);
    
            Thread notifyThread = new Thread(new Runnable() {
                
                @Override
                public void run() {
                 // 加锁,拥有lock的Monitor
                    synchronized (lock) {
                        // 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
                        // 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
                        System.out.println(Thread.currentThread().getName() + " notify 开始");
                        lock.notifyAll();
                        flag = false;
                        System.out.println(Thread.currentThread().getName() + " notify 完成 开始睡眠");
                        SleepUtils.second(5);
                        System.out.println(Thread.currentThread().getName() + "睡眠结束即将释放锁");
                    }
                }
            }, "NotifyThread");
            notifyThread.start();
        }
    }
    

    输出内容:


    image

    通过上面的示例代码,会发现使用wait/notfiy相关方法时需要注意一下细节:

    • 使用wait(),notify(),notifyAll()时需要先获取到该对象的锁。
    • 使用wait()方法会使线程进入阻塞状态,并且会释放锁。
    • 调用notify(),notifyAll()方法后,被通知的线程依旧不会从wait返回,需要调用notify()方法的线程释放锁之后,并且等待的线程重新获取到锁之后才会返回。
    • notify()方法是将等待队列中的一个等待线程从等待队列移到同步队列中,而notifyAll()方法是讲等待队列中的所有线程全部移到同步队列中

    wait & sleep 的区别

    从表面上来看,wait和sleep都能使当前线程进入阻塞状态,但两者还是有着本质上的区别:

    • wait和sleep方法都可以使线程进入阻塞状态,并且均是可中断方法,被中断后都会收到中断异常。
    • wait是Object方法,sleep是Thread独有的方法。
    • wait需要在同步方法中调用,而sleep不需要。
    • wait方法会释放锁,而sleep方法并不会释放锁。

    相关文章

      网友评论

          本文标题:四、【Java并发】线程间通信之wait,notify,noti

          本文链接:https://www.haomeiwen.com/subject/dgyaxctx.html