文章简介
很多人对的Thread.join的作用以及实现了解得很少,毕竟这个API我们很少使用。这篇文章仍然会结合使用及原理进行深度分析
内容导航
- 的Thread.join的作用
- 的Thread.join的实现原理
- 什么时候会使用的Thread.join
一、的Thread.join的作用
之前有人问过我一个这样的面试题
Java的中如何让多线程按照自己指定的顺序执行?
这个问题最简单的回答是通过的Thread.join来实现,久而久之就让很多人误以为的Thread.join是用来保证线程的顺序性的。感兴趣的可以加入我的学习圈子:142019080免费获取
下面这段代码演示了的Thread.join的作用
上面的代码,注意部分,大家可以把这行代码注释以后看看运行效果,在没有加加盟的时候运行的结果是不确定的。加了加入以后,运行结果按照递增的顺序展示出来。previousThread.join
的Thread.join的含义是当前线程需要等待previousThread线程终止之后才从的Thread.join返回。简单来说,就是线程没有执行完之前,会一直阻塞在加入方法处。
下面的图表现了加盟对于线程的作用
二、的Thread.join的实现原理
线程是如何被阻塞的?又是通过什么方法唤醒的呢?先来看看的Thread.join方法做了什么事情
从加入方法的源码来看,加入方法的本质调用的是对象中的等待方法实现线程的阻塞,等待方法的实现原理我们在后续的文章再说详细阐述。但是我们需要知道的是,调用等方法必须要获取锁,所以加入方法是被同步的修饰的,同步的修饰在方法层面相当于同步(这),这就是previousThread本身的实例。
有很多人不理解加入为什么阻塞的是主线程呢?不理解的原因是阻塞主线程的方法是放在previousThread这个实例作用,让大家误以为应该阻塞previousThread线程。实际上主线程会持有previousThread这个对象的锁,然后调用等方法去阻塞,而这个方法的调用者是在主线程中的。所以造成主线程阻塞。
第二个问题,为什么previousThread线程执行完毕就能够唤醒住线程呢?或者说是在什么时候唤醒的?
要了解这个问题,我们又得翻JDK的源码,但是如果大家对线程有一定的基本了解的话,通过等方法阻塞的线程,需要通过通知或者notifyAll的来唤醒。所以在线程执行完毕以后会有一个唤醒的操作,只是我们不需要关心。
接下来在热点的源码中找到,看看线程退出以后有没有做相关的事情来证明我们的猜想。thread.cpp
一下观察这行代码上的注释,唤醒处于等待的线程对象,这个是在线程终止之后做的清理工作,这个方法的定义代码片段如下ensure_join(this)
ensure_join方法中,调用唤醒所有等待thread锁的线程,意味着调用了加入方法被阻塞的主线程会被唤醒;到目前为止,我们基本上对联的原理做了一个比较详细的分析lock.notify_all(thread);
总结,的Thread.join其实底层是通过等待/ notifyAll的来实现线程的通信达到线程阻塞的目的;当线程执行结束以后,会触发两个事情,第一个是设置本地线程对象为空时,第二个是通过notifyAll的方法,让等待在previousThread对象锁上的等待方法被唤醒。
三、什么时候会使用的Thread.join
在实际应用开发中,我们很少会使用的Thread.join。在实际使用过程中,我们可以通过加入方法来等待线程执行的结果,其实有点类似未来/调用的功能。
我们通过以下伪代码来说明加入的使用场景
《架构师资料》领取方法
上图中的资料都是我精心录制视频,感兴趣的可以加入我的学习圈子:142019080免费获取