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

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

除了它们之外,还有用于线程调度、控制的方法,他们是sleep、yield、join方法,他们可以用于线程的协作,他们是围绕着线程的调度而来的

sleep方法

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

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

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

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

注意:

sleep不会释放锁,不会释放锁,不会释放锁

可以理解为他进入监视器这个房间之后,在这房间里面睡着了

与wait类似的是,sleep也是可中断方法(从方法签名可以看得出来,可能抛出InterruptedException),也就是说如果一个线程正在sleep,如果另外的线程将他中断(调用interrupt方法),将会抛出异常,并且中断状态将会擦除

所以对于sleep方法,要么自己醒来,要么被中断后也会醒来

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

如下,线程休眠500毫秒,主线程50毫秒打印一次状态

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

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

比如下面示例,两个阶段,第二个阶段将在第一个阶段执行之后才会执行

另外,你应该已经注意到sleep方法都有static修饰,既然是静态方法,在Thread中的惯例就是针对于:当前线程,当前线程,当前线程

yield方法

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

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

yield方法将会暂停当前正在执行的线程对象,并执行其它线程,他始终都是RUNNABLE状态

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

所以一次调用yield 并不一定会代表肯定会发生什么

借助于while循环以及yield方法,可以看得出来,也能一定程度上达到线程排序等待的效果

yield也是静态方法,所以,也是针对于当前线程,当前线程,当前线程。

join方法

三个版本的join方法

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

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

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

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

如果调整下顺序--->start 与 join的先后顺序,再次看下情况,可以发现顺序没有保障了

结论:

主线程main中调用启动线程(调用start),然后调用该线程的join方法,可以达到主线程等待工作线程运行结束才执行的效果,并且join要在start调用后

如何做到的?

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

主线程main中调用thread.join()方法,join方法相当于join(0),也就是

while (isAlive()) {

wait(0);

}

而这个wait(0)就相当于是this.wait(0),this就是我们自己创建的那个线程thread,看看方法的签名是不是有一个synchronized

isAlive()也是this.isAlive(),也就是如果当前线程alive(已经启动,但是未终止),那么将持续等待,等待的临界资源就是我们创建的这个线程对象本身

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

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

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

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

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

如果传递的参数不为0,将会走到下面的分支,或wait指定时长,与上面的逻辑一致,只不过是有指定超时时长而已

long delay = millis - now;

if (delay <= 0) {

break;

}

wait(delay);

now = System.currentTimeMillis() - base;

手动版本的等待结束

只是将join方法换成了同步代码块,锁对象为那个线程的实例对象thread,调用他的wait方法

从结果上看,效果一样

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

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

为什么?从我们目前来看,join方法就是以这个对象为锁,如果你自己在使用,又是wait又是notify(notifyAll)的,万一出现什么隐匿的问题咋办?

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

线程终止之后会调用自身this.notifyAll,以通知在其上等待的线程

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

为什么要在start之后?

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

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

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

所以,为什么join方法一定要在start之前?

就是因为这个isAlive方法的校验,你没有start,isAlive就是false,就不会同步等待,所以必须要先start,然后才能join

小结:

对于join方法,有两个关键:

  • 调用的哪个对象的join?
  • 在哪里调用的?

换一个说法:
join的效果是:一个线程等待另一个线程(直到结束或者持续一段时间)才执行,那么谁等待谁?

在哪个线程调用,哪个线程就会等待;调用的哪个Thread对象,就会等待哪个线程结束;

状态图回顾

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

总结

对于yield方法,比较容易理解,只是简单地对于CPU时间片的“礼让”,除非循环yield,否则一次yield,可能下次该线程仍旧可能会抢占到CPU时间片,可能方法调用和不调用没差别

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

join是实例方法,表示等待谁,是用于线程顺序的调度方法,可以做到一个线程等待另外一个线程,join有三个版本,指定超时时间或者持续等待直到目标线程执行结束,join也无需在同步方法(同步代码块)内

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

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

    • wait和sleep都会使线程进入阻塞状态,都是可中断方法,被中断后都会抛出异常
    • wait是Object的方法,sleep是Thread的方法
    • wait必须在同步中执行,sleep不需要(join底层依赖wait,但是不需要在同步中,因为join方法就是synchronized的)
    • wait会释放锁,sleep不会释放锁
    • wait(无超时设置的版本)会持续阻塞,必须等待唤醒,而sleep必然有超时,所以一定会自己醒来
    • wait 实例方法(Object),在对象上调用,表示在其上等待;sleep静态方法,当前线程

你能说出多线程中sleep、yield、join的用法及sleep与wait区别?的更多相关文章

  1. 进程?线程?多线程?同步?异步?守护线程?非守护线程(用户线程)?线程的几种状态?多线程中的方法join()?

    1.进程?线程?多线程? 进程就是正在运行的程序,他是线程的集合. 线程是正在独立运行的一条执行路径. 多线程是为了提高程序的执行效率.2.同步?异步? 同步: 单线程 异步: 多线程 3.守护线程? ...

  2. Oracle中 (+)与left join 的用法区别

    Oracle中 (+)与left join 的用法区别 原创 2017年01月11日 13:33:42 6648 select * from a,b where a.id=b.id(+); (+)写在 ...

  3. 关于多线程中sleep、join、yield的区别

    好了.说了多线程,那就不得不说说多线程的sleep().join()和yield()三个方法的区别啦 1.sleep()方法 /** * Causes the currently executing ...

  4. Java多线程中run(), start(), join(), wait(), yield(), sleep()的使用

    Run 每个Thread中需要实现的方法, 如果直接调用的话, 会是和单线程一样的效果, 要另起线程需要使用start(). start 新起线程调用run(). 主线程不等待直接往下执行 Yield ...

  5. 并发编程——线程中sleep(),yield(),join(),wait(),notify(),notifyAll()区别

    前言 今天简单的讲一讲线程中sleep(),join(),yield(),wait(),notify(),notifyAll()这些方法的使用以及区别. 不过在讲这些方法之前,需要简单的介绍一下锁池和 ...

  6. 多线程-3.sleep() yield() join()

    1.sleep()方法 jdk文档描述:Causes the currently executing thread to sleep (temporarily cease execution) for ...

  7. sql中的left join以及on、where关键字的区别

    创建两张表并插入一些数据 create table class( class_id int, class_name ), class_grade ) ); ,'语文','A'); ,'数学','B') ...

  8. Java中super的几种用法并与this的区别

    1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位. class Base { Base() { System.out.println("Base"); ...

  9. MySql语句中Union和join的用法

    Union UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每条 SELECT ...

随机推荐

  1. MVC三层架构的功能的简要说明

    MVC 介绍 MVC: Model 模型 ​ View 视图 ​ Controller 控制器 M (Model) : 模型 功能 DAO层 : 对数据库进行操作(CRUD) Service层 : 处 ...

  2. JAVA连接、操作数据库的DBHelper

    工厂模式的DBHelper 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.Prepa ...

  3. Linux下的ARP攻击-断网

    1.软件工具安装 1. nmap --网络嗅探工具 2. dsniff ( arpspoof )    --ARP嗅探工具 3. net-tools ( ifconfig ) --网络工具 sudo ...

  4. [bug] ERROR: Can't get master address from ZooKeeper; znode data == null

    排错 访问bigdata111:50070没显示 jps发现hdfs的namenode没启动 查看namenode日志发现9000端口被占用 查找占用端口的进程 杀死进程,或在配置文件中更改端口号 参 ...

  5. [bug] VMvare 虚拟机磁盘空间耗尽

    问题 VMvare虚拟机文件默认创建在C盘,装大程序的时,空间用尽就会报错,此时补救的办法是把虚拟机文件复制到空间足够的盘,再重新打开 最好一开始就选再有足够空间的盘里创建虚拟机 参考 https:/ ...

  6. 攻防世界(十二)upload1

    攻防世界系列 :upload1 1.打开题目,文件上传. 2.立即上传shell 1.php <?php @eval($_POST[root]); ?> 提示只能上传图片 3.burp改报 ...

  7. Ansible_处理失败的任务

    一.Ansible处理任务失败 1.管理play中任务错误 1️⃣:Ansible评估任务的返回代码,从而确定任务是成功还是失败 2️⃣:通常而言,当任务失败时,Ansible将立即在该主机上中止pl ...

  8. ScreenToGif: 屏幕录制神器

    ScreenToGif:一款小众但很好用的屏幕录制神器  牛人干货 2020-01-07 00:23:08 今天干货君给大家介绍一款电脑屏幕录制神器-ScreenToGif . ScreenToGif ...

  9. 007.kubernets的headless service配置和ingress的简单配置

    前面配置了servcie的nodepoint和clusterIP附在均衡 一 headless service配置 1.1 默认下的DNS配置 [root@docker-server1 deploym ...

  10. 9.11 strace:跟踪进程的系统调用 、ltrace:跟踪进程调用库函数

    strace 是Linux环境下的一款程序调试工具,用于检查一个应用程序所使用的系统调用以及它所接收的系统信息.strace会追踪程序运行时的整个生命周期,输出每一个系统调用的名字.参数.返回值和执行 ...