掃描下方二維碼或者微信搜索公衆號
菜鳥飛呀飛
,便可關注微信公衆號,閱讀更多Spring源碼分析
和Java併發編程
文章。java
public class JoinDemo {
public static void main(String[] args) {
System.out.println("肚子餓了,想吃飯飯");
Thread thread = new Thread(() -> {
System.out.println("開始作飯飯");
try {
// 讓線程休眠10秒鐘,模擬作飯的過程
Thread.sleep(10000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("飯飯作好了");
});
thread.start();
try {
// 等待飯作好
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 只有飯作好了,才能開始吃飯
System.out.println("開始吃飯飯");
}
}
複製代碼
線程之間的通訊
。CyclicBarrier和CountDownLatch這兩個類的做用的本質也是線程之間的通訊,在源碼分析中,咱們能夠發現,這兩個類的底層最終是經過AQS來實現的,而AQS中是經過LockSupport類的park()
和unpark()
方法來實現線程通訊的。而Thread類的join()則是經過Object類的wait
和notify()、notifyAll()
方法來實現的。join(0)
方法,參數傳0表示不限時長地等待
。join(long millis)方法的源碼以下。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");
}
// 當millis爲0時,表示不限時長地等待
if (millis == 0) {
// 經過while()死循環,只要線程還活着,那麼就等待
while (isAlive()) {
wait(0);
}
} else {
// 當millis不爲0時,就須要進行超時時間的計算,而後讓線程等待指定的時間
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
複製代碼
synchronized
關鍵字來保證線程安全,join(long millis)最終調用的是Object對象的wait()方法,讓主線程等待。這裏須要注意的是,synchronized關鍵字實現的隱式鎖,鎖的是this
,即thread這個對象(由於synchronized修飾的join(long millis)方法是一個成員方法,所以鎖的是實例對象),在Demo中,咱們是在main線程中,調用了thread這個對象的join()方法,因此這裏調用wait()方法時,調用的是thread這個對象的wait()方法,因此是main線程進入到等待狀態中。(調用的是thread這個對象的wait()方法,這一點很重要,由於後面喚醒main線程時,須要用thread這個對象的notify()或者notifyAll()方法
)。確定是成對出現的
,單獨使用它們毫無心義,那麼在join()方法的使用場景下,notify()或者notifyAll()方法是在哪兒被調用的呢?答案就是jvm
。jdk-jdk8-b120/hotspot/src/share/vm/runtime/thread.cpp
(筆者本地下載的是openJDK8的源代碼)。在thread.cpp文件中定義了不少和線程相關的方法,Thread.java類中的native方法就是在thread.cpp中實現的。JavaThread::exit(bool destroy_vm, ExitType exit_type)
方法(該方法的代碼在1730行附近)。exit()方法的源碼很長,差很少200行,刪除了無用的代碼,只保留了和今天主題有關的內容。源碼以下。void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
assert(this == JavaThread::current(), "thread consistency check");
// ...省略了不少代碼
// 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).
// 關鍵代碼就在這一行,從方法名就能夠推斷出,它是線程在退出時,用來確保join()方法的相關邏輯的。而這裏的this就是指的當前線程。
// 從上面的英文註釋也能看出,它是用來處理join()相關的邏輯的
ensure_join(this);
// ...省略了不少代碼
}
複製代碼
ensure_join()
方法中,接着找到ensure_join()
方法的源碼,源碼以下。(ensure_join()
方法的源碼也在thread.cpp文件當中。有興趣的朋友可使用JetBrains公司提供的Clion這款智能工具來查看JVM的源代碼,和IDEA產很少,按住option(或者Alt鍵)+鼠標左鍵,也能跟蹤到源代碼中)。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);
// 核心代碼在下面這一行
// 是否是很驚喜?果真見到了和notify相關的單詞,不用懷疑,notify_all()方法確定就是用來喚醒。這裏的thread對象就是咱們demo中的子線程thread這個實例對象
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
複製代碼
CountDownLatch
或者CyclicBarrier
,由於後二者的功能更增強大。// 這種寫法是等待/通知的經典範式。
while(條件判斷){
wait();
}
複製代碼