第九篇:线程间通信之等待/通知机制

一、等待/通知机制的实现

  使用wait()和notify()实现线程间的通信,wait使线程停止运行,而notify使停止的线程继续运行

  方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或者被中断为止。在调用wait()之前,线程必须获取该对象的对象级别锁,即只能在同步方法或者同步快中调用wait()方法在执行wait()方法后,当前线程释放锁。如果在调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateExcepion,它是RunntimeException的一个子类,因此,不需要try-catch语句进行捕获异常

  方法notify()也要在同步方法或者同步块中调用,即在调用前,线程也必须获取该对象的对象级别锁。如果在调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateExcepion。该方法用来通知那些可能等待该对象的对象锁的线程,如果有多个线程等待,则由线程规划器随机挑出其中一个呈wait()状态的线程,对其发出通知notify(一次notify方法只能通知一个执行wait方法的线程),并使它等待获取该对象的对象锁。需要注意的是,在执行notify()方法后,当前线程不会马上释放该对象锁呈wait()状态的线程并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait()状态所在线程才可以获取该对象锁。当第一个获取该对象锁wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次调用notify语句,则即便该对象已空闲,其他wait状态等待线程由于没有收到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll,如果发出notify操作时没有处于阻塞状态中的线程,那么该命令会被忽略。

  方法notifyAll()方法可以使所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入可运行状态。此时,优先级最高的那个想成优先执行,但有可能是随机执行,因为这取决于虚拟机的实现

二、等待/通知简单例子

例子一:

public class MyThread extends Thread {
    private Object lock;

    public MyThread(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("开始wait。。" + System.currentTimeMillis());
                lock.wait();
                System.out.println("结束wait。。" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  

public class MyThread2 extends Thread {
    private Object lock;

    public MyThread2(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            System.out.println("开始 notify。。" + System.currentTimeMillis());
            lock.notify();
            System.out.println("结束 notify。。" + System.currentTimeMillis());
        }

    }

    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread t = new MyThread(lock);
            t.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  运行main方法结果:

开始wait。。1546948292756
开始 notify。。1546948295759
结束 notify。。1546948295759
结束wait。。1546948295759

 结论:第一个线程在3s后被第二个线程唤醒

例子二:

public class ThreadB extends Thread {

    private MyList list;

    public ThreadB(MyList list) {
        this.list = list;
    }

    @Override
    public void run() {
        try {
            synchronized (list) {
                if (list.size() != 5) {
                    System.out.println("wait begin " + System.currentTimeMillis());
                    list.wait();
                    System.out.println("wait end " + System.currentTimeMillis());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  

public class ThreadA extends Thread {
    private MyList list;

    public ThreadA(MyList list) {
        this.list = list;
    }

    @Override
    public void run() {

        try {
            synchronized (list){
                for (int i = 0; i < 10; i++) {
                    list.add();
                    if (list.size() == 5){
                        list.notify();
                        System.out.println("已发出通知");
                    }

                    System.out.println("添加了" + (i + 1) + "个元素");
                    Thread.sleep(100);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        MyList service = new MyList();
        ThreadB b = new ThreadB(service);
        b.setName("b");
        b.start();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

    }
}

 运行main方法后结果:

wait begin 1546952240174
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
已发出通知
添加了5个元素
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
wait end 1546952241201

  从结果看,wait end在最后输出,这也说明了notify()方法执行后并不立即释放锁

三、线程状态转变的示意图:

参考https://www.aliyun.com/jiaocheng/820348.html

补充:每个锁对象都有两个队列,就绪队列和阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调度,反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。

四、当interrupt方法遇见wait方法

  当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常

public class Service {
    public void test(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("wait begin " + System.currentTimeMillis());
                lock.wait();
                System.out.println("wait end " + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            Object lock = new Object();
            Thread1 a = new Thread1(lock);
            a.start();
            Thread.sleep(5000);
            a.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  

public class Thread1 extends Thread {
    private Object lock;

    public Thread1(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
       Service service = new Service();
        service.test(lock);
    }
}

  运行main结果

wait begin 1546957612257
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at threadtest.xianchengjiantongxin.Service.test(Service.java:11)
	at threadtest.xianchengjiantongxin.Thread1.run(Thread1.java:17)

  总结:1、执行完同步代码块就会释放对象的锁

      2、执行同步代码块的过程中,遇到异常导致线程终止,锁与会被释放

     3、在执行同步代码块过程中,执行了所属对象的wait方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒。

五、wait(long)

  带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。也可以由其他线程唤醒,如果该锁设置了自动唤醒时间,但是在未到时间这段时间有其他线程用notify或notifyAll唤醒,则不等到达自动唤醒时间,直接唤醒。

 

转载于:https://www.cnblogs.com/moruoxuan/p/10226516.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值