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