Join()方法
作用
作用是:线程并行改为串行,并等待另一个线程执行结束
这句话看似简单,但实际上也容易 把人误导,认为只变得一个线程在执行所有方式,但实际上不然,本质上还是多个线程同时运行
原因/原理
Join()方法底层调用的是wait()方法,把当前主线程状态更改为等待状态,且等待子线程运行完毕唤醒
源码
public final synchronized void join(long var1) throws InterruptedException {
long var3 = System.currentTimeMillis();
long var5 = 0L;
if (var1 < 0L) {
throw new IllegalArgumentException("timeout value is negative");
} else {
if (var1 == 0L) { //由于上一步传入参数为0,因此调用当前判断
while(this.isAlive()) { //判断子线程是否存活
this.wait(0L); //调用wait(0)方法
}
} else {
while(this.isAlive()) {
long var7 = var1 - var5;
if (var7 <= 0L) {
break;
}
this.wait(var7);
var5 = System.currentTimeMillis() - var3;
}
}
}
}
public final synchronized void join(long var1, int var3) throws InterruptedException {
if (var1 < 0L) {
throw new IllegalArgumentException("timeout value is negative");
} else if (var3 >= 0 && var3 <= 999999) {
if (var3 >= 500000 || var3 != 0 && var1 == 0L) {
++var1;
}
this.join(var1);
} else {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
}
public final void join() throws InterruptedException {
this.join(0L);
}
可以从源码看出,最后还是调用了this.wait(0)L方法,让主线程进入等待状态。
再看下isAlive()方法,最后发现该方法是本地方法(调用最底层C++的函数方法), 判断子线程是否还存活
示例代码
/**
* @author Leo
* @description join案例,底层实际调用wait
* @createDate 2021/9/1 16:44
**/
public class JoinThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "..." + i);
}
}
/**
* 线程的join用法,运行结果是,完全输出线程1结果后,再输出main结果
* 让权,底层调用的wait方法
* 不涉及锁
*
* @param args
*/
public static void main(String[] args) throws Exception {
JoinThread thread = new JoinThread();
Thread t1 = new Thread(thread, "join线程");
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName() + "..." + j);
if (j == 1) {
t1.start();
t1.join();
}
}
}
}
日志打印
main...0
main...1
join线程...0
join线程...1
join线程...2
join线程...3
join线程...4
join线程...5
join线程...6
join线程...7
join线程...8
join线程...9
main...2
main...3
main...4
main...5
main...6
main...7
main...8
main...9
步骤
1. 主线程执行到j==1时,开启子线程且运行,同时主线程调用Join()方法
2. 主线程运行join()方法,先是调用isAlive()方法,判断子线程是否存活,如果存活,则调用wait()方法,使主线程自身进入等待状态
3. 子线程运行完毕,唤醒主线程
这里就存在问题,如何唤醒主线程
其实在《技术自查第四篇:线程基础篇》已经解答过该问题
我们都知道每个对象都存在唯一一个monitor对象,monitor对象是锁的关键,这里就不具体讲述Monitor对象了。
查看ObjectMonitor对象的C++源码
ObjectMonitor() {
_count = 0; //记录数
_recursions = 0; //锁的重入次数
_owner = NULL; //指向持有ObjectMonitor对象的线程
_WaitSet = NULL; //调用wait后,线程会被加入到_WaitSet
_EntryList = NULL ; //等待获取锁的线程,会被加入到该列表
}
对应的java类
由于主线程调用了wait()方法,所以被monitor监控器放到了_waitSet数组中,进入等待状态,一旦其他线程调用notify()或者notifyAll()方法即可唤醒主线程。
我们再看下Thread类的exit()
/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
* 这个方法由系统调用,当该线程完全退出前给它一个机会去释放空间。
*/
private void exit() {
if (group != null) { //线程组在Thread初始化时创建,存有创建的子线程
group.threadTerminated(this); //调用threadTerminated()方法
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
点击进入group.threadTerminated(this)方法,
/** Notifies the group that the thread {@code t} has terminated.
* 通知线程组,t线程已经终止。
*
void threadTerminated(Thread t) {
synchronized (this) {
remove(t); //从线程组中删除此线程
if (nthreads == 0) { //当线程组中线程数为0时
notifyAll(); //唤醒所有待定中的线程
}
if (daemon && (nthreads == 0) &&
(nUnstartedThreads == 0) && (ngroups == 0))
{
destroy();
}
}
}
最后发现底层调用了NotifyAll()方法