面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

Object中的wait、notify、notifyAll,可以用于线程间的通信,核心原理为借助于监视器的入口集与等待集逻辑

通过这三个方法完成线程在指定锁(监视器)上的等待与唤醒,这三个方法是以锁(监视器)为中心的通信方法

除了它们之外0 A F ~ p ! r $,还有用于线程调度、控制的方法,他们是slei m i 1 5 wep、yiek Q Y U Y ^ n Hld、join方法,他们 ! a ; H - W可以用于线程的协作,他们是围绕着? D x + d线程的调度而来的

sleep方法

有两个版本的sleep方法,看得出来,核心仍旧是native方法

非native方法只是进行了参数校验,接着仍旧是调用的native* a - } 6方法,这个情形与wait是类似的

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

接下来仔细看下,native版本的sleep

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。

注意:

sleep不会释放锁V G Z 4 + 2 a l,不会释放锁,不会释放锁,可以理解为他进入监视器这个E 5 D 5 m , Q $ !房间之后,在这房间P * o . y ; / k L里面睡着了

与wait类似的是,sleep也是可中断方法: 5 h 4 i(从方法签名可以看P ^ [得出来,可能抛出InterruptedException),也就是说如果一个线程正在sleep,如果另外的线程将他中断(调用int= f V F T verU z T G yrupt方法),将会抛出异常1 & 8,并且中断状态将会擦除

所以对于sleep方法,要么自己醒来,要么被中断后也会醒来。扩展:多线程基础体系知识清单

对于sleep始终有一个超时时间的设置,所以,尽管他是在监视器内睡着了,但是并不会导致死锁,因为他终究是要醒来的

v + b F T g W %下,线程& + _ !休眠500毫秒,主线程50毫秒打印一次状态

ps:sleep方法的调用结果为状态:TIMED_WAITING

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

借助于sleep方法,可以模拟线程的顺序执行

m l l Z k V如下面示例,两个阶段,第二个阶段将在第一个阶段执行之后才会执行

packaQ - !ge test1;
import java.lang.Thread.State;
public ca V I N w 6 P P lass T16 {
public&nbsY c ; Z Ip;static void main(String[]&nbs) | j p I Ap;args) {
//模拟执行任务的第一个阶段的执行
Thread stepOne = new Thread(() -> {1 . 4 o ^ v z
System.oq T qut.println(Thread.currentThread().getNak o 4 S s j K | Gme()+\" : 第一阶段任务开始执行\");
try {
T= h ? w V V 1 uhread.sleep(1000);
System.out.println(Thread.currentThread().getName()+\" : 第一阶段任务执d f C O ] H行结束\"! N W);
} ca* b ? l ] A t Stch (InterruptedException e) {
}
}, \"firstStage\");
stepOne.start();
//模拟任务第二个阶段的执行
Thread stepTwo = new Thread(() -> {
wh8 ) 0 o V $ F @ cile! = 6 (!State.TERMINAG D } f i !TED.equalse ;(stepOne.getState())) {
try {
Thread.sleep(100);
System.out.println(Thread.c: -urrentThr= ) * X P t ] Z Uead().getName()+\"&n+ O H , H j lbsp;: 我在等待第一阶段任务执行结束\");
} catch (InterruptedException: E S e) {
}
}
System.out.println(T* $ q = ( Q h ( Chread.currentThread().getName()+\" : 第二阶段任务执行结束\3 d E | 2");
}, \"secondStage\");
stepTwo.start();
}
}
面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

另外,你应该已经注意到sleep* ; u c X方法都有static修饰,既然是静态方法,在Thread中的惯9 I 6 w _例就是针对于:当前Z , _线程,当前线程,当前线程

yield方法

对于` q :sleep或者wait方法,他们都将进入特定的状态,伴随着状态的切换,也就意味着等待某些条件的发生,才能够继续,比如条件满足,或者到时间等

但是yield方法不涉及这些事情,他针对的是时间片的划分与调度,_ j 8 Z所以对开发者来说只是临时让一下,让一下他又不会死,就只是再等等。

yield方法将会暂停当前正在执行的线程对象,并执6 U { u行其它线程,他始终都是RUNNABL? } f SE状态。

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

不过要注意,可以认为yield只是一种建议性的,如果调用了yir _ L m feld方法,对CPU时间片的分配进行了“礼让”,它仍旧有可能继续获得时! @ @间片,并且继续执行

所以一次调用yield 并不一定会代表肯定h r h M 6 f l Z ;会发生什么

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

借助于w% 9 X c a i lhile循环以及yield方法,可以看得出来,也 D H m C _ M能一定程度上达到线程排B / ] c q y o h |序等待的效? u n :

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

yield也是静态方法,所以,也是针对于当前线程,当前线程,当前线程。扩展:多线程基础体系知识清单

join方法

三个版本的join方法

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

方法的实现过程,与wait也是非常类似,下面两个版本的方法一个调用join(0),一个参数校验后,调用join(millis),所以根本还是单参数版本的join方法

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

在方法深入介绍前先看个例子

一个线程,循环5次,每次sleep 1s,主线程中打印信息

从结果可以看到,主线程总是在线程执行之后,才会执行,也就是主线程在等] V m * a & z待我们创建的这个线程结束,结束了之后才会继续进行

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

如果调整下顺序--->st+ S $ bart 与 join的先后顺序,再次看下情况,可以发现顺序没& I l : u 4有保障了

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

结论:

主线程main中调用启动线程(调用start),然后调: K b B ; g } l e用该线程的join方` K ` . H 0法,可S X f .以达到主线程等待工作线程运行结束才执行的效果,并且join要在start调用后

如何做到% f ~ { % / B @的?

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

从上面源代码可以看得出来,内部调用了wait方法,所以也能明白为啥join也会抛出IntW L y MerruptedException了吧

主线程main中调] J 7 l` . 4 z J Cthread.join(^ d a ? } F +)方法,joj n e G ) r l tin方法相当于join(0),也就x q = + z [ } t

while (isAlive()) {
 &nbsn G S H L a K p; wait(0);
}

而这个wait(0)就相当_ S 1 [于是this.wait(( 1 : H 1 !0),this就是我们自己创建的那个线程thread,看看方法的; x ! I : X签名是不是有一个synchronized

isAliD n 2 G e |ve()也是this.isAlive(),也就是如果当前线程alive(已经启动,但是未终止),那么将持续等待,E } z & N P N x o等待的临界资源就是我们创建的这个线程对象本身。

推荐:多线程基础体系知识清单

所以这两行代码的含义就是:

该线程是否还存活?如果存活,调用join的那个线程将会在这个对象上进行等待(进入该线程对象的等待集)

也就是说调用一个线程的) } : - R L P G )join方法,就是在这个线程是等待,这个线程对象就是我们的锁对象(不要疑惑,Object都可以作为锁,Thread实例对象怎么不可以?)

肯定大家很奇怪,既然是等待,wait又不会自己醒来,那不是出问题了吗?

其实线程结束后,会调用this.notifyAll,所以主线程main会被唤醒

如果传递的参数不W G ( 3为0,将会走到下面的分支,会wait指定时长,与上面的! J K & ) m D逻辑一致,只不过是有指定超时时长而已

long delay = @ P k M [ 9 3 @ w;millis - now;
if (delay <= 0) {
   K W h 5 Obreak;
  }
wait(delay);
now = System.curd y 8 7 !rentTimeMillis() - base;

手动版本的等待结束

只是将join方法换成了同步代码块,锁对象为那个线程的实例对象thre} , E ) 6 Uad,调t $ W . / r k用他的wait方法

从结果上看,效果一样

(不过此处没有持续监测isAlive(),所以一旦主线程醒来,即使线程没有结束,也会继续,不能百分百确保main肯定等待线程结束)

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

不过要注意:注释中有说明,自己不要使用Thread类的实例对象作为锁对象,如果是现在这种场景,使用join即可

为什么?从我们目前来看,join方法就是以这个对象为锁,如果你自己在使用,又是wait又是not{ K m 7 t B O i ,ify(notifyAd ) @ ^ k y ull)的,万一出现什么隐匿的问题咋办?

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

所以join方法的原理就是:将指定的Thread实例对象作为锁对象,在其上进行同步,只要那个线程还活着,那么就会持续等待(或者有限时长)

线程终止之后M 9 # p $会调用自身this.notifyAll,以通知在; [ h r $ 6 a R其上等待的线程

简单说,只要他活着大k B = S家就都等着, 他死了会通知,所以效果就是在哪里调用了谁的join,哪里就要等待这个线程结束,才能继续

为什么要在start之后?

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

如上面所示,将join改造成同步代码块如下所示,如果这段同步代码在start方法之前

看下结果,没有等待指定线程结束q ! %,main主线程就结束了

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

因为如果还没有调用start方法,那么isAlive是false(已开始未结束),主线程根本就不会等待,所以继续执行,然后继续到下面的start,然后主线程结束

所以,为什么join方法一定要在sc 6 5 j U Ftart之前?

就是因为这个K E , S , f lisAlive方法的校验,你没有start,isAlivk n F J ( s Se就是false,就不f A G , ! / ! T会同b ! : f s步等待,所以必须要先start,然后才能join

小结:

对于join方法,有两个4 6 x n O关键:

  • 调用的H ( I o F # x哪个对象的join?
  • 在哪里调用的?

换一个说法:+ $ G K

join的效果是:一个线程等待另一个线程(直到2 H : H o B 结束或者持续一段时间)才执行,那x b A么谁等待谁?

在哪个线j 8 e & j S程调用,? R p ; B i e =哪个线程就会等待;调用的哪个Thread对象,就会等待哪个线程结束;

状态图回顾

在回顾下之前状态一文中的切换图,又了解了这几个方法后,应该对状态切换有了更全面的认识

面试官问我:能说出多线程中 sleep、yield、join的用法及区别吗

总结

对于yield方法,比! k 2 h , l ]较容易理解,r + g T只是简单地对于CPU时间片的“礼让”,除非循环yz m z T n o I Sield,否则一次yield,可能下次该线程仍旧可能会抢占到CPU时间片,可能方法调用和a [ i % U - Q不调用没差别

sleep是静态方法,针对当前线程,进入休眠状态,两个版本的sleep方法始终有时间参数,所以必然会在指定的时间内苏醒,他也不会释放锁,当然,sleep方法的调用非必须在同步方法(同步代码块)内

joiS p @ 1 # J / jn是实例方法,表示等待谁,是用于线程顺序的调度方法,可以做到一个线程等待另外一个, | V M线程,join有三个版本,指定超时时间或者持续等待直到目标线程执行结束,join也j v & X ]无需在同步方法(同步代码块)内

sleep和join都是可中断方法,被其他线程中断时,都会抛出InterruptedException异常,并且会醒来

join方法底层依赖wait,我们对比下wait与sleep

  • wait和sleep都会使线程进入阻塞状态,都是可中断方法,8 L . A ]被中断后都会抛出异常
  • wait是Object的方法,sleep是Thread的方法/ ( c
  • wait必须在同步中执行,sleep不需要(9 ? t x x Z p _join底层) q f依赖wait,但是不需要在同步中,因为join方法就是synchronized的)
  • wait会释放锁,sl@ C I ? * y N h &eep不会释放锁
  • wE ? B ~ ) *ait(无超时设置的版本)会持续阻塞,必须等待唤醒,而sleep必然有超时,所以一定会自己醒来
  • wait 实例方法(Object),在对象上调用,表示在其上等待;sleep静态方法,当前线程

Java知音,专注于Java实用文章推送,不容错过!

来源:cnblogs.com/noteless/p/10443446.html

上一篇

复联4中被漫威抛弃的剧本,钢铁侠被灭霸杀死,蜘蛛侠打响指

下一篇

嫁没嫁给爱情全凭一张脸!谢娜写书王自健挨揍大幂幂被瘦到脱相

你也可能喜欢

  • 暂无相关文章!

发表评论

您的电子邮件地址不会被公开。 必填项已用 * 标注

提示:点击验证后方可评论!

插入图片
返回顶部