多线程-线程的等待通知wait、notify

目录

1.什么是线程的等待通知

2.wiat()方法

    2.1 wait 做的事情:

2.2wait 结束等待的条件:

代码示例:

 2.3wait的三种重载方式

2.4 面试问题:wait()和sleep()之间的区别

3.notify()方法

3.1notify ?法是唤醒等待的线程.

 3.2wait 和notify之间的联系

 3.3notifyAll()


1.什么是线程的等待通知

        线程的等待通知是多线程编程中常用的一种机制,用于线程之间的协作和同步。在Java中,线程的等待通知通过使用wait()notify()notifyAll()方法来实现。

         由于线程之间是抢占式执?的, 因此线程之间执?的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执?先后顺序,所以我们需要wait()、notify()来实现这些功能。

2.wiat()方法

    2.1 wait 做的事情:

?
使当前执?代码的线程进?等待. (把线程放到等待队列中)

?
释放当前的锁

?
满??定条件时被唤醒, 重新尝试获取这个锁.

注意:wait 要搭配 synchronized 来使?. 脱离 synchronized 使? wait 会直接抛出异常.

2.2wait 结束等待的条件:

?
其他线程调?该对象的 notify ?法.

?
wait 等待时间超时 (wait ?法提供?个带有 timeout 参数的版本, 来指定等待时间).

?
其他线程调?该等待线程的 interrupted ?法, 导致 wait 抛出
InterruptedException
异常.

代码示例:

package 多线程;

public class ThreadDemo18 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();//new一个对象
        synchronized (object){//对对象进行加锁
            System.out.println("等待中···");
            object.wait();//当前线程会释放对象的锁,并进入等待状态。
            System.out.println("等待结束。");
        }
    }
}

       主线程会打印"等待中···",然后调用object.wait()方法,释放对象的锁,并进入等待状态。当其他线程调用了object.notify()object.notifyAll()方法时,主线程会被唤醒,然后重新获取对象的锁,并继续执行输出"等待结束。"。但是这个代码种没有notify()方法,所以没有重新唤起线程,就没有输出等待结束。

 

 2.3wait的三种重载方式

        第一种wait()重载方式:它是死等的,就是说如果没有notify()方法来唤醒它,它就一直处于阻塞状态。

        第二种wait()重载方式:它自己设定了一个超时的时间,单位是ms。就是说它最多等待到设定的时间,在这个时间内没有notify也不等了,直接会被唤醒。

       第三种wait()重载方式:方法使当前线程进入等待状态,直到其他线程调用该对象notify()notifyAll()方法唤醒它,或者指定的超时时间到达。这个方法允许设置纳秒级别的超时时间,除了毫秒级别的参数外,还可以指定纳秒级别的增量。

2.4 面试问题:wait()和sleep()之间的区别

   wait提供了一个带超时间的版本   

    sleep也是可以指定时间        都是时间到了解除阻塞。

wait和sleep都是可以提前被唤醒的

wait通过notify()来唤醒

sleep通过interrupt来唤醒

使用wait的主要目的,一定是不知道要等多少时间的前提下来使用的。所谓的超时间,其实是“兜底的”。

使用sleep,一定是知道要等多长时间,必须要等到那个时间才会被唤醒。 

3.notify()方法

3.1notify ?法是唤醒等待的线程.

?
?法notify()也要在同步?法或同步块中调?,该?法是?来通知那些可能等待该对象的对象锁的其 它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。

?
如果有多个线程等待,则有线程调度器随机挑选出?个呈 wait 状态的线程。(并没有 "先来后到")

?
在notify()?法后,当前线程不会?上释放该对象锁,要等到执?notify()?法的线程将程序执? 完,也就是退出同步代码块之后才会释放对象锁。

 代码示例:

package 多线程;

public class ThreadDemo19 {
    public static void main(String[] args) {
        //需要一个统一的对象进行加锁,wait,notify
        Object locker = new Object();
        
        Thread t1 = new Thread(()->{
            synchronized (locker){
                System.out.println("t1 wait 之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 wait 之后");
            }
        });
        Thread t2 = new Thread(()->{
            try {
                Thread.sleep(5000);
            synchronized (locker){
                System.out.println("t2 notify 之前");
                    locker.notify();
                System.out.println("t2 notify 之后");
                } 
                }
            catch (InterruptedException e) {
                e.printStackTrace();
                
            }
        });
    }
}

 1) t1 执行起来之后,就会先立即拿到锁,并且打印t1 wait 之前,并进入wait 方法(释放锁+阻塞等待)

2) t2 执行起来之后,先进行 sleep(5000)(这个 sleep 就可以让t1 能够先拿到锁)

3) t2 sleep 结束之后,由于t1 是wait 状态,锁是释放的.t2 就能拿到锁
接下来打印t2 notify 之前,执行 notify 操作,这个操作就能唤醒t1.(此时t1 就从 WAITING状态恢复回来了)

4)但是由于 t2 此时还没有释放锁呢,t1 WAITING 恢复之后,尝试获取锁,就可能出现一个小小的阻塞.

5) t2执行完 t2-motify 之后,释放锁,t2执行完毕.

所以输出结果如下:

 3.2wait 和notify之间的联系

wait 和notify之间是通过Object对象联系起来的。

Object1.wait()

Object2.notify() 此时如果用notify是无法被唤醒的,必须是两个对象一样才能唤醒。

Object1.wait()

Object2.wait()此时notify使用的哪个对象,哪个对象才能被唤醒。

 3.3notifyAll()

         唤醒这个对象上所有等待的线程.假设有很多个线程,都使用同一个对象 wait.针对这个对象进行 notifyAII,此时就会全都唤醒~~但是注意,这些线程在wait返回的时候,要重新获取锁,就会因为锁的竞争,使这些线程实际上是一个一个串行执行的.

具体使用方式如下:

  1. notifyAll()方法必须在同步代码块或同步方法中调用,并且只能应用于被synchronized关键字修饰的对象上。

  2. 当调用对象的notifyAll()方法时,该对象上所有等待的线程都会被唤醒,并尝试重新获取对象的锁。

  3. 被唤醒的线程会进入就绪状态,然后根据线程调度机制竞争获取锁。

  4. 只有获取到对象的锁的线程才能继续执行同步代码块中的内容,而其他未获取到锁的线程仍然处于阻塞状态,直到再次获得锁的机会。

希望大家多多支持!