0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

图解Java多线程中的wait()和notify()方法

Android编程精选 来源:CSDN-Killing Vibe 2023-03-22 09:29 次阅读

一、线程间等待与唤醒机制

wait()和notify()是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized 锁来使用。

多线程并发的场景下,有时需要某些线程先执行,这些线程执行结束后其他线程再继续执行。

比如: 一个长跑比赛,裁判员要等跑步运动员冲线了才能宣判比赛结束,那裁判员线程就得等待所有的运动员线程运行结束后,再唤醒这个裁判线程。

二、等待方法wait()

wait 做的事情:

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

释放当前的锁

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

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常.

887454d4-c7dc-11ed-bfe3-dac502259ad0.png

wait 结束等待的条件:

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

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

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

注意事项:

调用wait()方法的前提是首先要获取该对象的锁(synchronize对象锁)

调用wait()方法会释放锁,本线程进入等待队列等待被唤醒,被唤醒后不是立即恢复执行,而是进入阻塞队列,竞争锁

等待方法:

1.痴汉方法,死等,线程进入阻塞态(WAITING)直到有其他线程调用notify方法唤醒

88996dc8-c7dc-11ed-bfe3-dac502259ad0.png

2.等待一段时间,若在该时间内线程被唤醒,则继续执行,若超过相应时间还没有其他线程唤醒此线程,此线程不再等待,恢复执行。

88ab614a-c7dc-11ed-bfe3-dac502259ad0.png

调用wait方法之后:

88d99f06-c7dc-11ed-bfe3-dac502259ad0.png

三、唤醒方法notify()

notify 方法是唤醒等待的线程.

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

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

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

注意事项:

notify():随机唤醒一个处在等待状态的线程。

notifyAll():唤醒所有处在等待状态的线程。

无论是wait还是notify方法,都需要搭配synchronized锁来使用(等待和唤醒,也是需要对象)

89317672-c7dc-11ed-bfe3-dac502259ad0.png

四、关于wait和notify内部等待问题(重要)

对于wait和notify方法,其实有一个阻塞队列也有一个等待队列。

阻塞队列表示同一时间只有一个线程能获取到锁,其他线程进入阻塞队列

等待队列表示调用wait (首先此线程要获取到锁,进入等待队列,释放锁)

举个栗子:

现有如下定义的等待线程任务

privatestaticclassWaitTaskimplementsRunnable{
privateObjectlock;
publicWaitTask(Objectlock){
this.lock=lock;
}
@Override
publicvoidrun(){
synchronized(lock){
System.out.println(Thread.currentThread().getName()+"准备进入等待状态");
//此线程在等待lock对象的notify方法唤醒
try{
lock.wait();
Thread.sleep(1000);
}catch(InterruptedExceptione){
thrownewRuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"等待结束,本线程继续执行");
}
}
}

然后创建三个等待线程:

8961dd4e-c7dc-11ed-bfe3-dac502259ad0.png

由于同一时间只有一个线程(随机调度)能获取到synchronized锁,所以会有两个线程没竞争到锁,从而进入了阻塞队列。

这里假如t2先竞争到了锁,所以先会阻塞t1和t3:

89af03bc-c7dc-11ed-bfe3-dac502259ad0.png

又由于调用wait方法会释放锁,调用wait方法的线程t2就会进入等待队列,直到被notify唤醒或者超时自动唤醒。

89c6f49a-c7dc-11ed-bfe3-dac502259ad0.png

然后此时lock对象已经被释放了,所以t1和t3 又可以去竞争这个锁了,就从阻塞队列里面竞争锁。

这里假如t3 竞争到了锁,阻塞队列只剩下t1:

89da9c7a-c7dc-11ed-bfe3-dac502259ad0.png

然后t3运行到了wait方法,释放锁,然后进入等待队列:

89f51a96-c7dc-11ed-bfe3-dac502259ad0.png

然后重复这些操作~~,最后t1,t2,t3 都进入了等待队列中,等待notify线程唤醒(这里假设notify要放在这些线程start后的好几秒后,因为notify线程也是和这些线程并发执行的,所以等待队列中的线程随时可能被唤醒)

8a103be6-c7dc-11ed-bfe3-dac502259ad0.png

重点来了:

在等待队列中的线程,被notify唤醒之后,会直接回到阻塞队列去竞争锁!!!而不是直接唤醒~

举个栗子:

拿notifyAll()来举例,假如此时等待队列中有三个线程t1,t2,t3,那么调用notifyAll()会直接把它们三个直接从等待队列中进入到阻塞队列中:

8a28df34-c7dc-11ed-bfe3-dac502259ad0.png

然后再去竞争这个锁,去执行wait之后的代码~~

五、完整代码(仅供测试用)

privatestaticclassWaitTaskimplementsRunnable{
privateObjectlock;
publicWaitTask(Objectlock){
this.lock=lock;
}
@Override
publicvoidrun(){
synchronized(lock){
System.out.println(Thread.currentThread().getName()+"准备进入等待状态");
//此线程在等待lock对象的notify方法唤醒
try{
lock.wait();
Thread.sleep(1000);
}catch(InterruptedExceptione){
thrownewRuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"等待结束,本线程继续执行");
}
}
}
privatestaticclassNotifyTaskimplementsRunnable{
privateObjectlock;
publicNotifyTask(Objectlock){
this.lock=lock;
}
@Override
publicvoidrun(){
synchronized(lock){
System.out.println("准备唤醒");
//唤醒所有线程(随机)
lock.notifyAll();
System.out.println("唤醒结束");
}
}
}

publicstaticvoidmain(String[]args)throwsInterruptedException{
Objectlock=newObject();
Objectlock2=newObject();
//创建三个等待线程
Threadt1=newThread(newWaitTask(lock),"t1");
Threadt2=newThread(newWaitTask(lock),"t2");
Threadt3=newThread(newWaitTask(lock),"t3");
//创建一个唤醒线程
Threadnotify=newThread(newNotifyTask(lock2),"notify线程");
t1.start();
t2.start();
t3.start();
;
Thread.sleep(100);
notify.start();
//当前正在执行的线程数
Thread.sleep(2000);
System.out.println(Thread.activeCount()-1);
}

六、wait和sleep方法的区别(面试题):

wait方法是Object类提供的方法,需要搭配synchronized锁来使用,调用wait方法会释放锁,线程进入WAITING状态,等待被其他线程唤醒或者超时自动唤醒,唤醒之后的线程需要再次竞争synchronized锁才能继续执行。

sleep方法是Thread类提供的方法,调用sleep方法的线程进入TIMED_WAITING状态,不会释放锁,时间到自动唤醒。

总结

以上就是多线程场景下wait和notify方法的详解和注意事项了,码字不易,有帮助的话别忘了关注,点赞+收藏哦~

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2954

    浏览量

    104511
  • 多线程
    +关注

    关注

    0

    文章

    277

    浏览量

    19911
  • 代码
    +关注

    关注

    30

    文章

    4733

    浏览量

    68294
  • 线程
    +关注

    关注

    0

    文章

    504

    浏览量

    19638
  • WAIT
    +关注

    关注

    0

    文章

    4

    浏览量

    2504

原文标题:图解 Java多线程中的 wait() 和 notify() 方法

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Java多线程的用法

    本文将介绍一下Java多线程的用法。 基础介绍 什么是多线程 指的是在一个进程同时运行多个线程,每个线
    的头像 发表于 09-30 17:07 918次阅读

    Javasleep和wait的区别

    sleep()、suspend()、resume()方法不推荐使用,推荐使用wait()、notify()、notifyAll()。  sleep()方法是使
    发表于 11-25 11:50

    Java线程阻塞方法大全

    ()方法,当前线程转A入阻塞状态,直到线程B运行结束,线程A才由阻塞状态转为可执行状态。以上是Java线
    发表于 04-02 15:42

    Java线程唤醒与阻塞规则

    的join()方法,当前线程转A入阻塞状态,直到线程B运行结束,线程A才由阻塞状态转为可执行状态。以上是Java
    发表于 07-06 15:11

    java】两种方式实现线程通信:三个线程交替打印AABBCC

    ()、signal()方法。使用wait/notify进行线程通信只能够随机唤醒,增加了上下文的切换时间,使用await/signal可以实现精准唤醒,
    发表于 09-20 16:38

    Java基础学习多线程使用指南

    黑马程序员-----Java基础学习多线程
    发表于 10-08 14:10

    java多线程编程实例 (源程序)

    java多线程编程实例 import java.awt.*;import javax.swing.*; public class CompMover extends Object { 
    发表于 10-22 11:48 0次下载

    java多线程设计模式_结城浩

    JAVA多线程设计模式》通过浅显易懂的文字与实例来介绍JAVA线程相关的设计模式概念,并且通过实际的JAVA程序范例和UML图示来一一解说
    发表于 01-05 16:15 0次下载
    <b class='flag-5'>java</b><b class='flag-5'>多线程</b>设计模式_结城浩

    java多线程同步方法

    二、为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。举个例子,如果一个银行账户
    发表于 09-27 13:19 0次下载

    分析在javasleep和wait的不同

    的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。 在调用sleep()方法的过程线程不会释放对象锁。 而当调用wait()方法
    发表于 09-27 14:41 0次下载

    Java多线程总结之Queue

    Java多线程应用,队列的使用率很高,多数生产消费模型的首选数据结构就是队列。Java提供的线程安全的Queue可以分为 阻塞队列和非阻
    发表于 11-28 16:14 3282次阅读
    <b class='flag-5'>Java</b><b class='flag-5'>多线程</b>总结之Queue

    java学习——java面试【事务、锁、多线程】资料整理

    本文档内容介绍了基于java学习java面试【事务、锁、多线程】资料整理,供参考
    发表于 03-13 13:53 0次下载

    Java教程之零点起飞学Java线程资料说明

    多线程编程是提高应用程序性能的重要手段之一。Java平台从开始就被设计成为多线程环境,从语言级上支持多线程。在Java语言中,提供了创建、启
    发表于 02-20 10:41 3次下载
    <b class='flag-5'>Java</b>教程之零点起飞学<b class='flag-5'>Java</b>的<b class='flag-5'>线程</b>资料说明

    Java多线程永动任务 多线程异步任务项目解读

    1. 功能说明 2. 多线程任务示例 2.1 线程池 2.2 单个任务 2.3 任务入口 2.4 结果分析 2.5 源码地址 3. 写在最后 大家好,今天教大家撸一个 Java多线程
    的头像 发表于 10-19 11:46 1089次阅读

    java实现多线程的几种方式

    Java实现多线程的几种方式 多线程是指程序包含了两个或以上的线程,每个线程都可以并行执行不同
    的头像 发表于 03-14 16:55 557次阅读