一篇文章帶你徹底搞懂join的用法
java多執行緒裡的join,從字面意思來看是聯合,合併的意思,但如果面試時這麼回答,基本上可以斷定面試者還沒搞懂。join究竟能幹什麼,今天給出一個最通俗的解釋,那就是在多執行緒環境下實現暫時以單執行緒執行,或者說在並行執行的環境中實現暫時以序列執行。為了說明這個問題,我們看一段再常見不過的程式碼,程式碼內容是,讓三個執行緒分佈去列印一段內容
//程式碼塊
1
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DoSth());
Thread t2 = new Thread(new DoSth());
Thread t3 = new Thread(new DoSth());
t1.start();
t2.start();
t3.start();
System.out.println(
"主執行緒執行");
}
}
class DoSth implements Runnable {
@Override
public void run() {
int n =
5;
while (n >
0) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(
1);
} catch (InterruptedException e) {
e.printStackTrace();
}
n--;
}
}
}
執行結果如下:
執行緒1執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒1執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒1執行
執行緒1執行
執行緒1執行
可見三個執行緒各自並行執行,並無明確的先後順序。但如果我們在t.start()後面加上這行程式碼,
//程式碼塊2
t1.start();
t1.join();
看會出現看什麼樣的結果:
執行緒1執行
執行緒1執行
執行緒1執行
執行緒1執行
執行緒1執行
執行緒2執行
執行緒3執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
執行緒3執行
執行緒2執行
可以看到執行緒1執行結束之後執行緒2和3才開始執行,可見線上程1執行過程中,其他執行緒並未執行,執行緒1結束後,執行緒2,執行緒3開始並行執行,這就印證了前面的結論,即:join的作用是在多執行緒環境下暫時以單執行緒執行。明白了這一點,接下來的問題是,這個特性是怎麼實現的呢?我們跟到原始碼:可以看到
//程式碼塊3
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;
}
}
}
在第12行呼叫了wait,注意這裡的wait,它並不是指執行緒執行緒1物件執行wait,而是執行緒1的呼叫者,也就是相當於在主執行緒去執行wait,可等價理解為以下虛擬碼:
t1
.start();
while(t1.isAlive()){
Thread
.currentThread()
.doWait()
}
此時執行流程會在程式碼塊3的11-13行迴圈執行,當執行緒1執行完畢時,其生命週期結束,isAlive()返回false,11-13行退出迴圈,繼續執行下面的程式碼,此時又切換為並行執行狀態。對於以上執行效果,我們完全可以不用建立t1執行緒,而是把在主執行緒中直接去呼叫t1的核心邏輯,程式碼如下:
public static void main(String[] args) throws InterruptedException {
//Thread t1 =
new Thread(
new DoSth(),
"執行緒1");
Thread t2 =
new Thread(
new DoSth(),
"執行緒2");
Thread t3 =
new Thread(
new DoSth(),
"執行緒3");
//t1.start();
//t1.join();
new DoSth().run();
//直接呼叫業務邏輯,而不是分配執行緒去執行
t2.start();
t3.start();
System.out.println(
"主執行緒執行");
}
和之前的程式碼相比,本來需要在子執行緒t1中執行的內容,透過在主執行緒中執行達到了相同的效果,而這種特性,就體現了所謂的join,現在你明白為什麼叫join了吧?join在實際應用當中有什麼用呢?把以上程式碼改造一下,用一個例子來說明。
public
static
void
main(
String[] args) throws InterruptedException {
Thread t1 =
new Thread(
new DoSth(),
"小組1");
Thread t2 =
new Thread(
new DoSth(),
"小組2");
Thread t3 =
new Thread(
new DoSth(),
"小組3");
t1.start();
t2.start();
t3.start();
t1.
join();
t2.
join();
t3.
join();
System.
out.println(
"集合完畢");
}
執行結果:
小組3正在集合
小組1正在集合
小組2正在集合
小組3正在集合
小組1正在集合
小組2正在集合
小組3正在集合
小組1正在集合
集合完畢
當我們需要多個子執行緒分佈去完成各自的任務,並在這些子執行緒全部完成後,主執行緒做統一彙總時,join就派上用場了。不過細心的讀者會發現,這些子執行緒並未返回任何結果,如果我們需要返回結果供主執行緒使用時,該怎麼實現,針對這一需求,單靠實現Runnable的方式已經無法做到了,此時需要用到另外的介面:Feature和Callable。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2704761/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 乾貨預警,一篇文章帶你徹底搞懂 Laravel 框架的執行原理!!!Laravel框架
- 【架構視角】一篇文章帶你徹底吃透Spring架構Spring
- 一篇文章帶你搞懂 etcd 3.5 的核心特性
- 一篇文章讓你徹底掌握 shell 語言
- 面試必備|帶你徹底搞懂Python生成器面試Python
- 這篇文章讓你徹底搞懂ES6中的Class(全面解析)
- 看完讓你徹底搞懂Websocket原理Web
- 徹底搞懂徹底搞懂事件驅動模型 - Reactor事件模型React
- 一篇文章讓你徹底瞭解Java內部類Java
- 一篇文章帶你掌握Flex佈局的所有用法Flex
- 徹底搞懂https原理HTTP
- 【愣錘筆記】一篇小短文讓你徹底搞懂this、call、apply和bind筆記APP
- 帶你徹底弄懂Event LoopOOP
- 徹底搞懂Python中的類Python
- 徹底搞懂 Kubernetes 中的 Events
- 徹底搞懂 etcd 系列文章(三):etcd 叢集運維部署運維
- 帶你瞭解資料庫中JOIN的用法資料庫
- 什麼。你還沒有搞懂Spring事務增強器 ,一篇文章讓你徹底搞懂Spring事務,雖然很長但是乾貨滿滿Spring
- 五分鐘學Java:一篇文章帶你搞懂spring全家桶套餐JavaSpring
- 徹底理解Netty,這一篇文章就夠了Netty
- 徹底搞懂Bean載入Bean
- 徹底搞懂JavaScript作用域JavaScript
- 徹底搞懂 Git-RebaseGit
- 徹底搞懂JavaScript中的繼承JavaScript繼承
- 徹底搞懂HTTPS的加密機制HTTP加密
- 混合移動App乾貨:一篇就可以徹底搞懂!APP
- [譯]一篇幫你徹底弄懂NodeJs中的BufferNodeJS
- 一篇文章讓你搞懂原型和原型鏈原型
- 徹底搞懂IO多路複用
- 徹底搞懂 Channel 實現原理
- 一文徹底搞懂BERT
- 深入JavaScript系列(四):徹底搞懂thisJavaScript
- 一篇文章帶你搞定HashTable
- 徹底搞懂Scrapy的中介軟體(二)
- 徹底搞懂Scrapy的中介軟體(一)
- 徹底搞懂Scrapy的中介軟體(三)
- 徹底搞懂Object和Function的關係ObjectFunction
- Java的程式設計思想太抽象?3小時帶你徹底搞懂!(附思維導圖)Java程式設計抽象