java join()源码_Java Thread的join() 之刨根问底
0.Join()
線程的合并的含義就是 將幾個并行線程的線程合并為一個單線程執行,應用場景是 當一個線程必須等待另一個線程執行完畢才能執行時,Thread類提供了join方法來完成這個功能,注意,它不是靜態方法。
join有3個重載的方法:
void join():當前線程等該加入該線程后面,等待該線程終止。
void join(long millis):當前線程等待該線程終止的時間最長為 millis 毫秒。 如果在millis時間內,該線程沒有執行完,那么當前線程進入就緒狀態,重新等待cpu調度。
void join(long millis,int nanos):等待該線程終止的時間最長為 millis 毫秒 + nanos納秒。如果在millis時間內,該線程沒有執行完,那么當前線程進入就緒狀態,重新等待cpu調度。
1.使用方法
新建一個Thread類,重寫run()方法:
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("子線程執行完畢");
}
}
復制代碼
新建測試類,測試Join()方法:
public class TestThread {
public static void main(String[] args) {
//循環五次
for (int i = 0; i < 5; i++) {
MyThread thread = new MyThread();
//啟動線程
thread.start();
try {
//調用join()方法
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主線程執行完畢");
System.out.println("~~~~~~~~~~~~~~~");
}
}
}
復制代碼
輸出結果如下:
子線程執行完畢
主線程執行完畢
~~~~~~~~~~~~~~~
子線程執行完畢
主線程執行完畢
~~~~~~~~~~~~~~~
子線程執行完畢
主線程執行完畢
~~~~~~~~~~~~~~~
子線程執行完畢
主線程執行完畢
~~~~~~~~~~~~~~~
子線程執行完畢
主線程執行完畢
~~~~~~~~~~~~~~~
復制代碼
結果分析: 子線程每次都在主線程之前執行完畢,即子線程會在主線程之前執行。
代碼中循環5次是為了排除線程執行的隨機性,若不能說明問題,可以調大循環次數進行測試。
2.原理分析
查看Thread類源碼:
public final void join() throws InterruptedException {
//當調用join()時,實際是調用join(long)方法
join(0);
}
復制代碼
查看Join(long)方法源碼:
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) { //由于上一步傳入參數為0,因此調用當前判斷
while (isAlive()) { //判斷子線程是否存活
wait(0); //調用wait(0)方法
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
復制代碼
查看isAlive()方法源碼:
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
* 測試線程是否還活著。如果線程存活的話它就是已經開始,還沒有死亡的狀態。
* @return true if this thread is alive;
* false otherwise.
*/****
public final native boolean isAlive();
復制代碼
說明: 該方法為本地方法,判斷線程對象是否存活,若線程對象調用了start()方法,在沒有死亡的情況下此判斷為true。在上述例子中,由于調用了子線程的start()方法,并且沒有結束操作,因此判斷true。
查看wait()方法源碼:
public final native void wait(long timeout) throws InterruptedException;
復制代碼
說明: 該方法為本地方法,調用此方法的當前線程需要釋放鎖,并等待喚醒。在上述例子中,主線程調用子線程對象的join()方法,因此主線程在此位置需要釋放鎖,并進行等待。
wait()與wait(0)的區別:
查看wait()方法源碼,wait()方法只調用了wait(0),如下:
public final void wait() throws InterruptedException {
wait(0);
}
因此,wait()與wait(0)相同。
復制代碼
我們來擼一擼上述步驟: 主線程wait()等待,子線程調用了run()執行,打印“子線程執行完畢”。此時,主線程還沒被喚醒,還沒有執行下面的操作。那么,問題來了,誰?在什么時候?喚醒了主線程呢?
查看Thread類中存在exit()方法,源碼如下:
/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
* 這個方法由系統調用,當該線程完全退出前給它一個機會去釋放空間。
*/
private void exit() {
if (group != null) { //線程組在Thread初始化時創建,存有創建的子線程
group.threadTerminated(this); //調用threadTerminated()方法
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
復制代碼
通過debug,exit()在線程執行完run()方法之后會被調用,此時線程組中存在當前子線程,因此會調用線程組的threadTerminated()方法。
查看ThreadGroup.threadTerminated()方法源碼:
/** Notifies the group that the thread {@code t} has terminated.
* 通知線程組,t線程已經終止。
*
void threadTerminated(Thread t) {
synchronized (this) {
remove(t); //從線程組中刪除此線程
if (nthreads == 0) { //當線程組中線程數為0時
notifyAll(); //喚醒所有待定中的線程
}
if (daemon && (nthreads == 0) &&
(nUnstartedThreads == 0) && (ngroups == 0))
{
destroy();
}
}
}
復制代碼
通過此方法,將子線程從線程組中刪除,并喚醒其他等待的線程。在上述例子中,此時子線程被銷毀,并釋放占用的資源,并喚醒等待中的線程。而在join()方法中等待的主線程被喚醒,并獲得鎖,打印“主線程執行完畢”。
3.總結
Join()方法,使調用此方法的線程wait()(在例子中是main線程),直到調用此方法的線程對象(在例子中是MyThread對象)所在的線程(在例子中是子線程)執行完畢后被喚醒。
由于線程的啟動與銷毀其實是由操作系統進行操作,所以在描述的時候刻意略去,如果有疑惑的地方,可以查看C++編寫的本地方法。
復制代碼
總結
以上是生活随笔為你收集整理的java join()源码_Java Thread的join() 之刨根问底的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java frame 显示图片_java
- 下一篇: java 反射 orm_Java-反射机