目录
前言
探究Thread类join方法的使用及源码分析,并从jvm底层分析实现原理
一、Thread.join的作用是什么?
是线程之间通信的一种方式,具体来说就是让一个线程等待另一个线程执行完成
二、用法
使用join的情况:
class ThreadJoinTest {
public static void main(String[] args) throws InterruptedException {
Thread t =new Thread(()->{
System.out.println("我是子线程");
try {
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
//使当前线程也就是执行t.join()的线程(主线程)等待线程t执行完成
t.join();
System.out.println("我是主线程");
}
}
--- 运行结果
我是子线程
我是主线程
Process finished with exit code 0
不使用join的情况:
class ThreadJoinTest {
public static void main(String[] args) throws InterruptedException {
Thread t =new Thread(()->{
System.out.println("我是子线程");
try {
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
System.out.println("我是主线程");
}
}
---运行结果
我是主线程
我是子线程
Process finished with exit code 0
使用join后主线程会等待线程t执行完毕才会继续执行,不使用join是无法保证线程t的执行顺序的
二、原理分析
1.先看下join的源码
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;
}
}
}
可以看到最终线程实现等待是调用了Object的wait方法,所以join方法加了synchronized ,wait是基于对象的Monitor实现的,objectMonitor是Monitor的c++实现,每个java对象都会关联一个objectMonitor下面我们看下objectMonitor的结构
ObjectMonitor() {
// 多线程竞争锁进入时的单向链表
ObjectWaiter * volatile _cxq;
//处于等待锁block状态的线程,会被加入到该列表
ObjectWaiter * volatile _EntryList;
// _header是一个markOop类型,markOop就是对象头中的Mark Word
volatile markOop _header;
// 抢占该锁的线程数,约等于WaitSet.size + EntryList.size
volatile intptr_t _count;
// 等待线程数
volatile intptr_t _waiters;
// 锁的重入次数
volatile intptr_ _recursions;
// 监视器锁寄生的对象,锁是寄托存储于对象中
void* volatile _object;
// 指向持有ObjectMonitor对象的线程
void* volatile _owner;
// 处于wait状态的线程,会被加入到_WaitSet
ObjectWaiter * volatile _WaitSet;
// 操作WaitSet链表的锁
volatile int _WaitSetLock;
// 嵌套加锁次数,最外层锁的_recursions属性为0
volatile intptr_t _recursions;
}
objectMonitor维护了_WaitSet变量,这个变量存放的就是已经持有对象synchronized锁并执行wait方法的线程,用上面的示例代码解释就是主线程执行t.join时,先获取线程对象t的synchronized锁具体就是用cas把主线程对象放入ObjectMonitor._owner,执行wait方法后,主线程的线程对象会放入线程对象t关联的ObjectMonitor._waitSet,同时清空ObjectMonitor._owner完成锁的释放,从而实现主线程的等待。
2.从join方法可以看出执行wait方法后就结束了,那么主线程是怎么被唤醒的呢?
主线程被唤醒应该是t线程执行完之后发生的,具体怎么实现的就需要从jvm底层分析线程执行过程,线程是由jvm通过和操作系统交互最终由操作系统调度执行的。下面就是一个线程执行结束要执行的exit方法,它是jvm底层源码,由C++实现
//源码位置https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/javaThread.cpp
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
// ...
// Notify waiters on thread object. This has to be done after exit() is called
// on the thread (if the thread is the last thread in a daemon ThreadGroup the
// group should have the destroyed bit set before waiters are notified).
// 根据源码注释可知大概就是唤醒等待在当前线程objectMonitor对象上waiters
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);
// 兄弟们这就是真相
// thread就是当前线程就是代码示例中的t线程
// 此处对应就是执行Object的notifyAll方法
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
至此join实现线程等待以及等待的线程怎么被唤醒的大致底层原理已经明确
总结
join是通过Object的wait实现等待,然后通过jvm和操作系统调度,最终调用notifyAll唤醒等待的线程来实现的