点击上方 “ 布衣码农 ” ,免费订阅~选择“ 设为星标 ”,第一时间免费获得更新~

「布衣码农」用不到却又不得不学习了解的底层方法+1。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方法,可以模拟线程的顺序执行比如下面示例,两个阶段,第二个阶段将在第一个阶段执行之后才会执行

package test1;import java.lang.Thread.State;public class T16 {public static void main(String[] args) {    //模拟执行任务的第一个阶段的执行    Thread stepOne = new Thread(() -> {    System.out.println(Thread.currentThread().getName()+" : 第一阶段任务开始执行");    try {        Thread.sleep(1000);        System.out.println(Thread.currentThread().getName()+" : 第一阶段任务执行结束");        } catch (InterruptedException e) {      }    }, "firstStage");    stepOne.start();    //模拟任务第二个阶段的执行    Thread stepTwo = new Thread(() -> {      while (!State.TERMINATED.equals(stepOne.getState())) {        try {        Thread.sleep(100);        System.out.println(Thread.currentThread().getName()+" : 我在等待第一阶段任务执行结束");        } catch (InterruptedException e) {        }      }      System.out.println(Thread.currentThread().getName()+" : 第二阶段任务执行结束");    }, "secondStage");    stepTwo.start();    }}

另外,你应该已经注意到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静态方法,当前线程

··················END··················

注:非技术讲解配图均来源于网络

期待分享

如果对你有用

可以点个 「在看」 或者分享到 「 朋友圈 」 哦

你「在看」吗? ↓↓

full outer join 与full join的区别_sleep、yield、join都是干啥的? sleep与wait有啥区别?中篇[十五]...相关推荐

  1. oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念 Nested loop join: Outer - phpStudy...

    浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...

  2. Java中sleep,wait,yield,join的区别

    sleep() wait() yield() join()用法与区别 1.sleep()方法 在指定时间内让当前正在执行的线程暂停执行,但不会释放"锁标志".不推荐使用. slee ...

  3. 【Hive】left semi join(exists、in)和 left join 区别

    left semi join(exists.in)和 left join 区别 left semi join 基本认识 对比 执行计划 小结 left semi join 基本认识 LEFT SEMI ...

  4. full join 和full outer join_多表关联:公式展开、join、过滤条件的顺序

    这是在实现多表关联时想到的. 我们现在这套体系,实现多表关联比较复杂.如果Superset能官方支持多表关联,不知道会是什么样的方案,复杂度如何. 在公式这个层面,没有关联条件,只有两个列.或者多个列 ...

  5. mysql+join+合计_图解MySQL里的各种 JOIN,看完不懂来找我!

    点击关注上方"SQL数据库开发", 设为"置顶或星标",第一时间送达干货作者:码志 链接:https://mazhuang.org/2017/09/11/joi ...

  6. 2021年大数据Flink(四十五):​​​​​​扩展阅读 双流Join

    目录 扩展阅读  双流Join 介绍 Window Join Interval Join ​​​​​​​代码演示1 ​​​​​​​代码演示2 重点注意 扩展阅读  双流Join 介绍 https:// ...

  7. Oracle的join默认为,Oracle中的三种Join方法详解

    这里将为大家介绍Oracle中的三种Join方法,Nested loop join.Sort merge join和Hash join.整理出来以便帮助大家学习. 基本概念 Nested loop j ...

  8. 线程join_Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)

    点击上方"Coder编程",选择"置顶公众号" 技术文章第一时间送达! 并发编程.png 每天进步一点,不做curd工程师与Api调用工程师 欢迎访问 个人博客 ...

  9. left join 一对多_MYSQL 连接查询算法:JOIN语句在 MYSQL 内部到底是怎么执行的

    前言 我们从一个问题引入今天的主题. 在日常业务开发中,我们可能经常听到 DBA 对我们说"不要"(注意:不是禁止)使用 join,那么为什么 DBA 对 join 这么抵触呢?是 ...

最新文章

  1. 高德拉特难题:悬赏5000美金的一道作业排序问题
  2. GCN代码超详解析Two-stream adaptive graph convolutional network for Skeleton-Based Action Recognition(三)
  3. html 数据库 编写学生表,用sql语句创建学生表如何做
  4. 【转】深入理解Windows消息机制
  5. 如何知道电脑服务器操作系统,电脑如何查看服务器操作系统
  6. iOS JSPatch 热修复使用
  7. [WM C++]从资源文件中加载显示png/jpg图片
  8. 整天说Code Review重要,你知道应该关注哪些关键点吗?
  9. 深入浅出 Javascript API(一)--基本框架
  10. h5 右下角浮动按钮_Flutter 浮动按钮-FloatingActionButton的使用
  11. 基于java 网页的宠物店管理系统
  12. VM虚拟机的安装及安装操作系统
  13. python基础(八):封装、继承、多态
  14. 洛谷 P3435 [POI2006]OKR-Periods of Words(KMP+记忆化搜索)
  15. 2020牛客暑期多校训练营(第九场)K-The Flee Plan of Groundhog
  16. 力扣OJ 剑指 Offer(1-30)
  17. IT开发团队分工及内容
  18. 计算机管理器用户怎么打开文件,资源管理器怎么设置默认打开我的电脑
  19. CVPR、ECCV 2020 两大会议论文分类索引
  20. 网卡不叫eth0,而叫ens33、ens160、eno1 or enp0s*?

热门文章

  1. selenium启动 IE11方法
  2. [Go语言]从Docker源码学习Go——init()方法和identifier首字母大小写区分
  3. 心得14--jsp遍历所有数据标签与转义标签
  4. hwnd = 0 各种粗心大意啊!
  5. GWT(Google Web Tookit) Eclipse Plugin的zip下载地址(同时提供GWT Designer下载地址)
  6. 《动手学深度学习》 第二天 (自动求梯度)
  7. php伪静态失败,php伪静态后html不能访问怎么办
  8. xshell 上下左右键乱码和退格键失效
  9. html文本改,编辑html格式文本可改成txt格式(可以替换或更换某文本)新手
  10. 青岛农商银行计算机防病毒应用培训,青岛农商银行胶州支行多元化培训提升安防管理水平...