wait, sleep, notify, notifyall, join

本文详细解析了Java中线程控制的关键方法,包括wait、notify、sleep与interrupt的区别及应用场景,帮助开发者更好地理解和掌握线程间的协作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解释一:

wait

导致当前的线程等待,直到其他线程调用此对象的 notify方法或 notifyAll 方法。当前的线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行
sleep
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。该线程不丢失任何监视器的所属权。
wait与sleep

Wait是Object类的方法,范围是使该Object实例所处的线程。

Sleep()是Thread类专属的静态方法,针对一个特定的线程。

Wait方法使实体所处线程暂停执行,从而使对象进入等待状态,直到被notify方法通知或者wait的等待的时间到。Sleep方法使持有的线程暂停运行,从而使线程进入休眠状态,直到用interrupt方法来打断他的休眠或者sleep的休眠的时间到Wait方法进入等待状态时会释放同步锁(如上例中的lock对象),而Sleep方法不会释放同步锁。所以,当一个线程无限Sleep时又没有任何人去interrupt它的时候,程序就产生大麻烦了notify是用来通知线程,但在notify之前线程是需要获得lock的。另个意思就是必须写在synchronized(lockobj) {...}之中。wait也是这个样子,一个线程需要释放某个lock,也是在其获得lock情况下才能够释放,所以wait也需要放在synchronized(lockobj) {...}之中。

Sleep与interrupt

interrupt是个很暴力的方法,打断一个线程的Sleep时并不需要获得该线程的lock。虽然暴力却也有暴力的用处。在一个线程无时限sleep的时候也只有interrupt能够唤醒他。在interrupt的时候会抛出InterruptedException,这个Exception是由Thread 类自动抛出的。因此Interrupt带有强烈的阻塞味道。

wait与interrupt

interrupt同样可以打断wait的等待,与打断sleep不同的是,被打断的wait的线程在重新获得lock之前是不会抛出InterruptedException。

resume和suspend已经被Java遗弃,因为他们天生会引起线程的死锁。

suspend是个贪婪的家伙,当一个线程在suspend的时候,线程会停下来,但却仍然持有在这之前获得的锁定。其他线程无法使用他锁定的任何资源,除非这个挂起的线程被resume之后,他才会继续运行。对于线程的同步,使用wait与notify要安全的多。



解释二:

一、sleep & wait

1. 两者来自不同的类(分别是Thread和Object)

2.sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或方法

3.wiat只能在同步控制方法或者同步控制块使用,而sleep可以在任何地方使用

4.sleep必须捕获异常,而wait不需要

二、wait和notify为什么会封装在Object类中,而不是像sleep方法在Thread中?

wait和notify的本质是基于条件对象,而且只能由已经获得锁的线程调用。java的每个Object都有一个隐式锁,这个隐式锁关联一个Condition条件对象,                     线程拿到这个隐式所(比如进入synchronized代码区域),就可以调用wait,语义是Condition条件对象上等待,其他的线程可以在这个Condition条件对象上等待 ,           等满足条件之后,就可以调用notify或者notifyAll来唤醒所有在此条件对象上等待的线程。

三、死锁(产生死锁的必要条件)

互斥条件:一个资源每次只能被一个进程使用

请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

不剥夺条件:进程已获得的资源,在未使用完之前,不能强制剥夺

循环等待条件:若干进程之间形成一种头尾相接的循环资源关系。

     这四个条件时死锁的必要条件,只有系统发生死锁,这些条件必然成立,只要上述条件之一不满足,就不发生死锁。

四、常识

 1.在java中所有线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

 2.在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾回收线程。因为每当使用java命令执行一个类的时候,实际上启动了一个jvm,

每个jvm其实就是在操作系统中启动了一个线程

  3.理解java编译器的线程处理和jvm。有助于编程高效、性能更好的java代码。

 五、 java中wait/notify机制 
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,
而且不是按优先级。notify()方法和wait()方法的基本思想是给方法或代码块提供一种相互通信的方式,而这些方法或者代码块同步于某个特定对象。
代码块可以调用wait()方法来将自身的操作挂起,直到同一个对象上的其他同步方法或同步代码块以某种方式将其改变,并调用notfiy()方法来通知此代码块改变已经完成。
一个线程一般会因为它所同步的对象的某个属性没有设置,或者某个条件没有满足而调用wait()方法,这些由另一个线程的动作决定。
最简单的情况可能是资源因为正被另一个线程修改而繁忙,还有其他的可能情况。
 

通 常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线 程 downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了 任务 后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。
以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如:
synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();}  

当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:
synchronized(obj) {condition = true;obj.notify();} 

需要注意的概念是:
◆调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {...} 代码段内。
◆调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {...} 代码段内唤醒A。
◆当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
◆如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
◆obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
◆当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。

 

七、join

       join方法的功能就是使异步执行的线程变成同步执行。

join() method suspends the execution of the calling thread until the object called finishes its execution.

也就是说,t.join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续;通常用于在main()主线程内,等待其它线程完成再结束main()主线程。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值