如果现在有这样一个场景:在主线程中创建两个线程T1和T2,通过start启动线程后。我们想T1结束了再执行T2,等T1和T2都执行结束,才继续执行主线程,怎么能做到呢?
其实很简单,通过join就可以实现:
public class JoinDemo {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0; i < 10000; i++){
count ++;
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 10000; i++){
count ++;
}
});
t1.start();
t1.join();
t2.start();
t2.join();
System.out.println(count);
for (int i = 0; i < 10000; i++){
count ++;
}
System.out.println(count);
}
}
像上面这样,通过join就能实现让主线程阻塞,等待T1执行完了再执行T2,T2完成再执行T1.
那到底是如何实现的呢?其实可以猜想一下,如果想让主线程阻塞,等待某一个线程执行完成后再继续执行,可以通过让主线程Wait在某一个锁上,等到子线程执行完成后,在唤醒主线程即可。那join是如何实现的呢?看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;
}
}
}
会发现,果然,Join确实是通过wait实现的,是以被等待的线程为锁对象,将主线程wait在以被等待线程为锁对象的对象锁上。
有wait,必定会有notify,那在什么时候notify呢?既然主线程是为了等待被等待线程执行结束再继续执行,那应该是在被等待线程退出的时候调用notify。所以,应该找找JVM线程退出时候的源码:
在Thread.cpp中找到exit方法,会发现,在exit方法中调用了ensure_join方法,从注释上可以看出,这个方法就是为了在线程退出之前,唤醒wait在当前线程的对象锁上的其他线程。
继续看ensure_join方法的实现:
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();
}
发现,最终确实是调用了notifyAll方法。
为了加深对join的理解,其实我们为了实现文章开头的那个场景,并不需要调用join方法,只需要将主线程wait在被等待的线程的线程锁对象上即可,同样能实现和join相同的效果,因为join就是这样做的,所以我们可以这样去写(当然实际中肯定是用封装好的join最简单明了):
public class JoinDemo {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
new JoinDemo().fun();
}
private void fun() throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0; i < 10000; i++){
count ++;
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 10000; i++){
count ++;
}
});
t1.start();
synchronized(t1){
t1.wait();
}
// t1.join();
t2.start();
synchronized (t2){
t2.wait();
}
// t2.join();
System.out.println(count);
for (int i = 0; i < 10000; i++){
count ++;
}
System.out.println(count);
}
}