如果現在有這樣一個場景:在主線程中創建兩個線程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);
}
}