博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程的等待线程结束(join)和谦让(yield)是什么情况?
阅读量:4072 次
发布时间:2019-05-25

本文共 3816 字,大约阅读时间需要 12 分钟。

前言

本文隶属于专栏《100个问题搞定Java并发》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和参考文献请见

正文

为什么需要 join?

在很多情况下,线程之间的协作和人与人之间的协作非常类似。

一种非常常见的合作方式就是分工合作。

以我们非常熟悉的软件开发为例,在一个项目进行时,总是应该有几位号称是“需求分析师”的同事,先对系统的需求和功能点进行整理和总结,以书面形式给出份需求说明或者类似的参考文档,然后,软件设计师、研发工程师オ会一拥而上,进行软件开发。

如果缺少需求分析师的工作输出,那么软件研发的难度可能会比较大。

因此,作为名软件研发人员,总是喜欢等待需求分析师完成他应该完成的任务后,才愿意投身工作。

简单地说,就是研发人员需要等待需求分析师完成他的工作,然后才能进行研发。

将这个关系对应到多线程应用中,很多时候,一个线程的输入可能非常依赖于另外个或者多个线程的输出,此时,这个线程就需要等待依赖线程执行完毕,才能继续执行

JDK 提供了 join() 操作来实现这个功能。

join 是什么?

如下所示,显示了两个 join ()方法:

public final void join()throws InterruptedExceptionpublic final synchronized void join(long millis)throws InterruptedException

第一个 join ()方法表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕。

第二个方法给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行。

英文 join 的翻译,通常是加入的意思。

这个意思在这里也非常贴切。

因为一个线程要加入另外一个线程,最好的方法就是等着它一起走

join() 方法的本质是让调用线程 wait() 方法在当前线程对象实例上。(详情请见下面的源码解读)

yield 是什么?

另外一个比较有趣的方法是 Thread.yield ,它的定义如下:

public static native void yield();

这是一个静态方法,一旦执行,它会使当前线程让出 CPU 。

但要注意,让出 CPU 并不表示当前线程不执行了。

当前线程在让出 CPU 后,还会进行 CPU 资源的争夺,但是是否能够再次被分配到就不一定了。

因此,对 Thread.yield 方法的调用就好像是在说:“我已经完成了一些最重要的工作了,我可以休息一下了,可以给其他线程一些工作机会啦!”

如果你觉得一个线程不那么重要,或者优先级非常低,而且又害怕它会占用太多的 CPU 资源,那么可以在适当的时候调用 Thread.yield 方法,给予其他重要线程更多的工作机会。

源码(JDK8)

/** * 等待此线程死亡。  * * 此方法的调用与调用的行为完全相同 join(0)  * * @throws InterruptedException–如果任何线程中断了当前线程。引发此异常时,将清除当前线程的中断状态。 */public final void join()throws InterruptedException{
join(0); }
/** * 等待此线程死亡的时间最多为给定的毫秒数。 * * millis 为0表示永远等待。  * * 当前实现使用了调用 this.wait 的循环,基于 this.isAlive 为条件。 * * 当线程终止时,调用 this.notifyAll 方法。建议应用程序不要在线程实例上使用wait、notify或notifyAll。  * * @param   millis 毫秒–以毫秒为单位的等待时间  * * @throws IllegalArgumentException–如果millis的值为负  * *          InterruptedException–如果任何线程中断了当前线程。引发此异常时,将清除当前线程的中断状态。 */public final synchronized void join(long millis)throws InterruptedException{
long base=System.currentTimeMillis(); long now=0; //millis的值不能为负 if(millis< 0){
throw new IllegalArgumentException("timeout value is negative"); } //millis 为0表示永远等待 if(millis==0){
while(isAlive()){
wait(0); } }else{
while(isAlive()){
long delay=millis-now; if(delay<=0){
break; } // 线程等待指定时间 wait(delay); now=System.currentTimeMillis()-base; } }}

关于 wait 的源码解读请参考我的博客——

/** * 对调度程序的一个提示,表示当前线程愿意放弃当前对处理器的使用。 * * 调度程序可以随意忽略此提示。  * * yield 是一种启发式的尝试,旨在改善线程之间的相对进程,否则会过度使用CPU。 * * 它的使用应该与详细的分析和基准测试相结合,以确保它实际具有预期的效果。  * * 使用这种方法很少合适。 * * 它可能对调试或测试有用,因为它可能有助于再现由于竞争条件而产生的bug。 * * 在设计并发控制结构(如java.util.concurrent.locks包中的结构)时,它可能也很有用。 */public static native void yield();

实践

package com.shockang.study.java.concurrent.thread.join;public class JoinDemo {
public volatile static int i = 0; public static class AddThread extends Thread {
@Override public void run() {
for (i = 0; i < 10000000; i++) ; } } public static void main(String[] args) throws InterruptedException {
AddThread at = new AddThread(); at.start(); at.join(); System.out.println(i); }}
package com.shockang.study.java.concurrent.thread.join;/** * 中断状态可以检测,并在应用上作出相应 * 如果应用不相应中断,则T1永远不会退出 * * @author Shockang */public class YieldDemo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted!"); break; } Thread.yield(); } } }; t1.start(); Thread.sleep(2000); t1.interrupt(); }}

转载地址:http://kugji.baihongyu.com/

你可能感兴趣的文章
关于checkbox的点击事件和返回值
查看>>
TableDnd(JQuery表格拖拽控件)应用进阶
查看>>
[转]基于C语言的Javascript引擎探索
查看>>
[转]VLD扩展使用指南
查看>>
[转]BigPipe学习研究
查看>>
[转]用 awstats分析 Nginx 日志的一些记录
查看>>
[转]细说明 linux sudo 不要密码
查看>>
Yii分析3:Yii日志记录
查看>>
关于HTTP头(header)的Vary的解释
查看>>
JQuery中止(abort)Ajax请求
查看>>
【转】能说明你的Javascript技术很烂的五个原因
查看>>
[转]开源中最好的Web开发的资源
查看>>
Docker上部署SpringBoot项目并推送镜像到Docker Hub上---以MacOS为例
查看>>
bibtex I was expecting a `,‘ or a `}‘ 问题解决
查看>>
sql server中各类范式的理解
查看>>
进程同步
查看>>
Python中列表元素删除
查看>>
Eclipse中改动jsp文件的编码格式以及字体大小
查看>>
二分查找与递归式二分查找
查看>>
全排列问题
查看>>