下面展示最基本的代码示例
main 为主线程
t1 为子线程
public static void main(String[] args) throws InterruptedException {
// 初始化状态
Thread t1 = new Thread(() -> {});
// 可运行 / 运行状态,至于什么时候运行由底层平台控制
t1.start();
// 阻塞状态,等待t1线程执行完毕
t1.join();
}
Thread.join() 源码分析
join有三个重载方法,分别为join()
、join(long millis)
、join(long millis, int nanos)
public final void join() throws InterruptedException {}
public final synchronized void join(long millis) throws InterruptedException {}
public final synchronized void join(long millis, int nanos) throws InterruptedException {}
join()
方法等价于join(0)
join(long millis, int nanos)
方法最终也是调用join(long millis)
所以我们就拿join(long millis)
来做介绍
/**
* 最多等待millis毫秒该线程终止。超时为0则意味着永远等待。
* 此方法使用循环方式判断isAlive()方式调用this.watit。当前程终止时,调用this.notifyAll方法。
* 建议应用程序不要在Thread实例上使用wait、notify或notifyAll
*
* @param millis - 等待时间(以毫秒为单位)
* @throws IllegalArgumentException - 如果millis的值为负
* @throws InterruptedException - 如果任何线程中断了当前线程。抛出此异常时清除当前线程的中断状态。
*/
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
其中的核心方法就是
// while(isAlive())是为了防止子线程伪唤醒(spurious wakeup),
//只要子线程(t1)线程没有结束,父线程(main)就需要继续等下去。
while (isAlive()) {
wait(0);
}
isAlive()
是一个 final 的 native方法
/**
* 测试此线程是否存活。 如果线程已启动且尚未死亡,则该线程处于活动状态
*
* @return 如果此线程还活着,则为true; 否则为false;
*/
public final native boolean isAlive();
执行步骤
public static void main(String[] args) throws InterruptedException {
// 初始化状态
Thread t1 = new Thread(() -> {});
// 可运行 / 运行状态,至于什么时候运行由底层平台控制
t1.start();
// 阻塞状态,等待t1线程执行完毕
t1.join();
}
-
创建线程 t1
new Thread(() -> {})
-
调用
t1.start();
启动线程 -
同时调用
t1.join();
方法, 主线程(main
)会进入等待状态- join方法中的核心代码就是调用
while (isAlive())
isAlive()
方法就是判断t1线程是否执行结束- 根据上述代码来看,线程里面是调用空的方法
() -> {}
- 所以
isAlive()
会直接返回false
- 此时
t1.join();
方法内的代码执行结束,会调用 jvm C++的源码,执行notifyAll()
- 代码返回到主线程的
t1.join();
继续往下执行 - 结束
- join方法中的核心代码就是调用
如果代码修改为
public static void main(String[] args) throws InterruptedException {
// 初始化状态
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 可运行 / 运行状态,至于什么时候运行由底层平台控制
t1.start();
// 阻塞状态,等待t1线程执行完毕
t1.join();
}
那么在调用t1.join()
方法时
- 创建线程 t1
new Thread(() -> {//...})
- 调用
t1.start();
启动线程 - 同时调用
t1.join();
方法, 主线程(main
)会进入等待状态- join方法中的核心代码就是调用
while (isAlive())
isAlive()
方法就是判断t1线程是否执行结束- 根据上述代码来看,线程执行的方法要1秒钟左右才回执行结束
- 所以
isAlive()
会直接返回true
,调用wait()
继续等待 - 在
while (isAlive())
一秒左右后会返回false
- 此时
t1.join();
方法内的代码执行结束 - 代码返回到主线程的
t1.join();
继续往下执行 - 结束
- join方法中的核心代码就是调用
总结
在看完源码之后,还是比较容易理解的。
但是我在源码中没有找到代码在执行完join
后调用了notifyAll
的地方,带着这个问题在网上找了一下才发现,最终答案在jvm C++源码里
void JavaThread::run() {
...
thread_main_inner();
}
void JavaThread::thread_main_inner() {
...
this->exit(false);
delete this;
}
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
...
ensure_join(this);
...
}
static void ensure_join(JavaThread* thread) {
// We do not need to grap the Threads_lock, since we are operating on ourself.
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
// Clear the native thread instance - this makes isAlive return false and allows the join()
// to complete once we've done the notify_all below
java_lang_Thread::set_thread(threadObj(), NULL);
lock.notify_all(thread); // 重点
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
其中的重点代码就是lock.notify_all(thread);
这里就是调用notifyAll()
的地方了