并发编程之线程之间的通信

版权声明:转载请注明地址,谢谢配合 https://blog.csdn.net/pulong0748/article/details/88064810

等待wait()和通知notify()、notifyAll()

简单分析

线程之间用来通信的两个方法:wait()等待notify()通知.它们是object类里的方法。如下:

方法名称 描述
notify() 通知一个在对象上等待的相乘,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁。
notifyAll() 通知所有等待在该对象上的线程。
wait() 调用该方法的线程由RUNNING进入WAITING状态,只有等待另外线程的通知或被终端才会返回,需要注意,调用wait()方法后,会释放对象的锁。
wait(long) 超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。
wait(long, int) 对于超时时间更细粒度的控制,可以达到纳秒。

我们进入到Object源码里去看下wait():

 public final void wait() throws InterruptedException {
        wait(0);
    }
 public final native void notify();

可以发现wait()方法里调用了wait(0),参数表示超时时间,为0的话就是一直等待,知道当前线程被通知之后才会结束等待。可以发现notify是一个本地方法。

wait():当在一个对象实例上调用wait()方法后,当前对象实例所在的线程就会在这个对象实例上等待。直白的讲就是线程A里面调用了obj.wait(),那么线程A就不会继续执行了,转为等待状态,直到其他线程B调用了obj.notify(),或者A线程再调用obj.notify(),所以很明显这两个方法可以实现线程之间的通信。

下面是一个很简单的等待通知的例子:

public class SimpleWait {
    final static Object object = new Object();
    public static class T1 extends Thread{
        @Override
        public void run() {
            synchronized (object){
            System.out.println("T1线程开启");
            try {
                System.out.println("T1线程进入等待");
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("T1线程继续执行");
                try {
                    Thread.sleep(2_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T1线程结束");
        }
    }
    }

    public static class T2 extends Thread{
        @Override
        public void run() {
            synchronized (object){
                System.out.println("T2开启,通知一个线程");
                object.notify();
                try {
                    // 虽然通知了(这里指的是等待队列的任意线程)
                    // 但是模拟一下T2线程还需要经历2秒左右才能处理完
                    // 也就是说2秒后才会释放锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T2结束");
            }
        }
    }
    public static void main(String[] args) {
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        t2.start();
    }
}

上面的代码大概是这个意思:开启两个线程T1和T2,T1先拿到锁之后,进入等待(释放锁),此时T2拿到锁,通知等待队列里的任意线程(这里就一个T1),之后执行完T2线程的剩下动作,T2释放锁,T1继续从等待的地方执行。从而完成T1和T2之间的线程通信。


用一个图大概的说明下wait()、notify()的工作流程:

1547819436454

  1. T1线程拿到锁之后,当调用wait()或者join()方法,将进入等待状态,同时释放锁

1547819804954

  1. T2抢到锁之后,处于运行状态,当调用notify方法,假设这里只有T1等待中,那么T1从等待状态进入到阻塞状态,因为它还没有得到锁,也就意味着T2虽然通知了T1,但是还没有执行完它剩下的程序。当T2执行完,T1顺利拿到锁之后,进入到运行状态,接着之前等待状态那边继续执行。

1547820616782

  1. 除了notify()方法,还有一个notifyAll方法,它的用法跟notify基本类似,但是他不像notify随机的通知一个线程,它是通知所有线程,拿到锁的那个线程将会继续处于运行状态

注意点:

  1. Object.wait()、notify()、notifyAll()必须包含在synchronized语句中,它们必须获得目标对象的监视器
  2. Object.wait()方法和Thread.sleep()方法都可以让线程等待若干时间,wait()方法在等待的时候会释放锁,但是Thread.sleep不会释放任何资源

猜你喜欢

转载自blog.csdn.net/pulong0748/article/details/88064810
今日推荐