wait方法 与 notify方法

1. 线程状态图

在这里插入图片描述

这是线程的7状态模型图,常见的7大状态之间的转换关系也在上面。多线程之间的通信主要用到4个方法,wait()、wait(long time)、notify()、notifyAll(),其他方法在多线程基础中都有介绍。

  1. wait():作用是使当前线程从调用处中断并且释放锁转入等待队列,直到收到notify或者notifyAll的通知才能从等待队列转入锁池队列,没有收到停止会一直死等。

  2. wait(long time):相比wait多了一个等待的时间time,如果经过time(毫秒)时间后没有收到notify或者notifyAll的通知,自动从等待队列转入锁池队列。

  3. notify():随机从等待队列中通知一个持有相同锁的一个线程,如果没有持有相同锁的wait线程那么指令忽略无效。注意是持有相同锁,并且是随机没有固定的,顺序这一点在生产者消费者模型中很重要,会造成假死的状态。

  4. notifyAll():通知等待队列中的持有相同锁的所有线程,让这些线程转入锁池队列。如果没有持有相同锁的wait线程那么指令忽略无效。

  5. wait的两个方法都需要注意中断的问题,wait中断是从语句处中断并且释放锁,当再次获得锁时是从中断处继续向下执行。

  6. notify 和 notifyAll方法通知是延迟通知,必须等待当前线程体执行完所有的同步方法/代码块中的语句退出释放锁才通知wait线程。

  7. 这四个方法都必须在获得锁的情况下才能调用,否则会出现非法监视状态异常。



2. 基本使用

2.1、wait立即释放锁 与 notify/notifyAll延迟通知
class MyService{
    private Object  obj = new Object();
    public void waitfun(){
        try{
            synchronized (obj){
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                obj.wait();
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    public void notifyfun(){
        try{
            synchronized (obj){
                System.out.println("begin notify: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                obj.notify();
                System.out.println("end   notity: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(String name, MyService myService) {
        super(name);
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.waitfun();
    }
}

class ThreadB extends Thread{
    private MyService myService;
    public ThreadB(String name, MyService myService) {
        super(name);
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.notifyfun();
    }
}

public class Demo {

    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        ThreadA a = new ThreadA("Thread", myService);
        a.start();
        

        Thread.sleep(1000);
        ThreadB b = new ThreadB("Thread", myService);
        b.start();
    }
}

在这里插入图片描述

分析:a线程调用wait方法进行中断,立即释放锁;main线程睡眠1000ms后,b线程启动获得对象锁开始通知,然后执行.notify语句,最后输出了通知完毕;执行完了b线程同步方法/代码块里的所有语句才进行通知,a线程收到通知进行执行,从中断处继续向下执行最后得到上图结果。


2.2、wait(long time)的自动唤醒
class Service{ }

class ServiceA extends Thread{
    private Service service;

    public ServiceA(String name, Service service) {
        super(name);
        this.service = service;
    }

    @Override
    public void run() {
        try{
            synchronized (service){
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                service.wait(5000);
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
            System.out.println(Thread.currentThread().getName()+"等待5000ms后自动唤醒!");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class ServiceB extends Thread{
    private Service service;

    public ServiceB(String name, Service service) {
        super(name);
        this.service = service;
    }

    @Override
    public void run() {
        try{
            synchronized (service){
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();

        ServiceA a = new ServiceA("ThreadA",service);
        ServiceB b = new ServiceB("ThreadB",service);

        a.start();
        Thread.sleep(100);
        b.start();
    }
}

在这里插入图片描述

方法主体中并没有对wait的线程进行唤醒停止,但是经过time秒后A线程自动唤醒了。wait(long time)方法在经过time(毫秒)之后的等待即使没有收到 通知 会自动从等待队列转入锁池队列。


2.3、提前唤醒,处于wait(long time)的线程如果正处在time等待的时间内提前收到通知会直接进入锁池队列,time时间提前失效。
class Service{ }

class ServiceA extends Thread{
    private Service service;

    public ServiceA(String name, Service service) {
        super(name);
        this.service = service;
    }

    @Override
    public void run() {
        try{
            long begin = 0;
            long end = 0;
            synchronized (service){
                begin = System.currentTimeMillis();
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + begin);
                service.wait(8000);
                end = System.currentTimeMillis();
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + end);
            }
            System.out.println(Thread.currentThread().getName() + "等待" + (end-begin) + "ms后唤醒!");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class ServiceB extends Thread{
    private Service service;

    public ServiceB(String name, Service service) {
        super(name);
        this.service = service;
    }

    @Override
    public void run() {
        try{
            synchronized (service){
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                service.notify();
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        ServiceA a = new ServiceA("ThreadA",service);
        ServiceB b = new ServiceB("ThreadB",service);

        a.start();
        Thread.sleep(1000);
        b.start();
    }
}

在这里插入图片描述


2.5、单一通知:notify方法随机的从等待队列中唤醒一个持有相同锁的线程进入锁池队列争抢同步锁,如果没有持有相同锁的wait的线程则notify指令无效。
class MyService{
    private Object  obj = new Object();
    public void waitfun(){
        try{
            synchronized (obj){
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                obj.wait();
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    public void notifyfun(){
        try{
            synchronized (obj){
                System.out.println("begin notify: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                obj.notify();
                System.out.println("end   notity: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(String name, MyService myService) {
        super(name);
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.waitfun();
    }
}

class ThreadB extends Thread{
    private MyService myService;
    public ThreadB(String name, MyService myService) {
        super(name);
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.notifyfun();
    }
}

public class notifyone {

    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        for(int i = 1;i <= 5;i++){
            new ThreadA("Thread"+i, myService).start();
        }

        Thread.sleep(1000);
        for(int i = 6;i <= 12;i++){
            new ThreadB("Thread"+i,myService).start();
            Thread.sleep(500);
        }
    }
}

在这里插入图片描述

2.6、全部通知:notifyAll方法将等待队列中所有的持有相同锁的线程唤醒进入锁池队列争抢同步锁。
/*
    notifyAll全部通知,通知顺序是随机的
    由JVM决定,并没有固定顺序
 */

class MyService1{
    private Object obj = new Object();
    public void waitfun(){
        try{
            synchronized (obj){
                System.out.println("begin   wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                obj.wait();
                System.out.println("end     wait: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    public void notifyfun(){
        try{
            synchronized (obj){
                System.out.println("begin notify: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                obj.notifyAll();
                System.out.println("end   notity: " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class ThreadA1 extends Thread{
    private MyService1 myService;
    public ThreadA1(String name, MyService1 myService) {
        super(name);
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.waitfun();
    }
}

class ThreadB1 extends Thread{
    private MyService1 myService;
    public ThreadB1(String name, MyService1 myService) {
        super(name);
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.notifyfun();
    }
}


public class notifyall {
    public static void main(String[] args) throws InterruptedException {

        MyService1 myService = new MyService1();
        for(int i = 1;i <= 5;i++){
            new ThreadA1("ThreadA"+i,myService).start();
        }
        Thread.sleep(5000);

        new ThreadB1("noticeThread",myService).start();
    }
}

在这里插入图片描述



3. 异常释放锁

学习synchronized时,有几种情况当同步方法 / 块中遇到异常会提前结束线程体。

  • sleep + interrupt:这两个方法没有先后顺序,无论哪个先执行,发生异常时线程体提前中止。

  • 手动制造异常:当执行到某个程度的时候,如果想提前结束直接利用throw new Exception()。

  • 使用stop方法直接摧毁线程,这个方法已经废弃了不推荐使用。

  • 使用return 提前结束线程体的运行,但是这种方法并不是异常。

3.1、手动制造异常提前终止
import java.util.ArrayList;
class MyList{
    volatile private ArrayList<String> list = new ArrayList<>();            //保证可见性

    public void add(){
        list.add("123");
    }

    public int size(){
        return list.size();
    }
}
class MyService{

    private MyList list = new MyList();
    private Object obj = new Object();

    public void waitMethod(){
        try{
            synchronized (obj){
                if (list.size() != 5){
                    System.out.println("begin wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
                    obj.wait();
                    System.out.println("end   wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    public void notifyMethod(){
        try{
            synchronized (obj){
                for(int i = 1;i <= 10;i++){
                    list.add();
                    System.out.println("add添加了" + i +"个元素.");
                    if(list.size() == 5){
                        obj.notify();
                        throw new Exception();		//没有异常list中将会有10个元素,制造异常则只有5个
                        System.out.println("发出通知,等我执行完再执行.");		//不会执行
                    }
                }
            }
        }catch (Exception e){
         //   e.printStackTrace();
        }
    }
}

class ThreadA extends Thread{

    private MyService myService;

    public ThreadA(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.notifyMethod();
    }
}
class ThreadB extends Thread{

    private MyService myService;

    public ThreadB(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.waitMethod();
    }
}

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        ThreadA a = new ThreadA(myService);
        a.setName("ThreadA");
        ThreadB b = new ThreadB(myService);
        b.setName("ThreadB");
        b.start();
        Thread.sleep(1000);
        a.start();
    }
}

在这里插入图片描述

3.2、wait + interrupt方法提前终止线程
class A{
    private Object obj;

    public A(Object object) {
        this.obj = object;
    }

    public void method(){
        synchronized (obj){
            System.out.println("begin wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
            try {
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end   wait : "+ Thread.currentThread().getName() + " " + System.currentTimeMillis());
        }
    }
}

class MyThread extends Thread{
    private A a;

    public MyThread(A a) {
        this.a = a;
    }

    @Override
    public void run() {
        a.method();
    }
}
public class Test02 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        A a = new A(object);

        MyThread myThread = new MyThread(a);
        myThread.start();

        Thread.sleep(5000);
        myThread.interrupt();
    }
}

在这里插入图片描述

4. 总结

  1. sleep睡眠是抱锁睡眠,不会进入等待队列而是进入阻塞状态。如上图

  2. wait()方法会立即释放锁中断,进入等待队列。唤醒后仍然需要去争抢获得同步锁才能继续从中断处向下执行。

  3. wait(long time)方法会在time毫秒后自动唤醒,如果提前遇到通知会提前进入等待队列。

  4. notify / notifyAll 通知是随机顺序通知的,并没有固定的顺序进行通知具体的某个线程。并且只通知持有相同锁的wait线程。

  5. 灵活使用异常能够帮助线程体的提前终止。

  • 13
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
`wait()` 和 `notify()` 方法是 Java 中用于线程间通信的两个重要方法。`wait()` 方法用于让线程进入等待状态,并释放锁,而 `notify()` 方法则用于唤醒等待状态的线程。这两个方法必须在同步方法或同步块中使用,否则会抛出 `IllegalMonitorStateException` 异常。 `wait()` 方法的调用会让线程进入等待状态,直到有其他线程调用了同一个对象的 `notify()` 或 `notifyAll()` 方法来唤醒它。在等待期间,线程会释放它持有的锁,以便其他线程可以进入同步块执行。`wait()` 方法可以使用以下两种方式调用: - `wait()`:让线程一直等待,直到其他线程调用了 `notify()` 或 `notifyAll()` 方法唤醒它。 - `wait(long timeout)`:让线程等待一段时间,如果在等待期间没有其他线程调用 `notify()` 或 `notifyAll()` 方法唤醒它,那么线程会自动醒来。 `notify()` 方法用于唤醒等待状态的线程。它会随机地唤醒一个等待状态的线程,如果有多个线程都在等待同一个对象的锁,那么只有其中一个线程会被唤醒。`notifyAll()` 方法则会唤醒所有等待状态的线程。 以下是一个简单的示例,展示了如何使用 `wait()` 和 `notify()` 方法进行线程间通信: ```java class MyThread implements Runnable { private final Object lock; public MyThread(Object lock) { this.lock = lock; } @Override public void run() { synchronized (lock) { System.out.println("Thread " + Thread.currentThread().getName() + " is waiting"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread " + Thread.currentThread().getName() + " is awake"); } } } public class Main { public static void main(String[] args) throws InterruptedException { Object lock = new Object(); Thread t1 = new Thread(new MyThread(lock)); Thread t2 = new Thread(new MyThread(lock)); t1.start(); t2.start(); Thread.sleep(1000); synchronized (lock) { lock.notify(); } } } ``` 在这个示例中,我们创建了两个线程 `t1` 和 `t2`,它们都在同一个对象 `lock` 上等待。在主线程中,我们等待 1 秒钟后调用了 `notify()` 方法来唤醒一个等待状态的线程。由于 `notify()` 方法是随机唤醒一个线程,因此我们无法确定哪个线程会被唤醒。在这个示例中,我们可以看到其中一个线程被唤醒并输出了 "Thread X is awake" 的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值