java 线程执行结束_Java_如何等待子线程执行结束
本程序的數(shù)據(jù)有可能是如下:
main thread work start
sub thread start working.
main thread work done.
now waiting sub thread done.
sub thread stop working.
now all done.
忽略標號, 當然輸出也有可能是1和2調(diào)換位置了. 這個我們是無法控制的. 我們看下線程的join操作, 究竟干了什么.
這里是調(diào)用了
public?final?synchronized?void?join(long?millis)
方法, 參數(shù)為0, 表示沒有超時時間, 等到線程結(jié)束為止. join(millis)方法里面有這么一段代碼:
說明, 當線程處于活躍狀態(tài)的時候, 會一直等待, 直到這里的isAlive方法返回false, 才會結(jié)束.isAlive方法是一個本地方法, 他的作用是判斷線程是否已經(jīng)執(zhí)行結(jié)束. 注釋是這么寫的:
Tests if this thread is alive. A thread is alive if it has?been started and has not yet died.
可見, join系列方法可以幫助我們等待一個子線程的結(jié)束.
那么要問, 有沒有另外一種方法可以等待子線程結(jié)束? 當然有的, 我們可以使用并發(fā)包下面的Future模式.
Future是一個任務執(zhí)行的結(jié)果, 他是一個將來時, 即一個任務執(zhí)行, 立即異步返回一個Future對象, 等到任務結(jié)束的時候, 會把值返回給這個future對象里面. 我們可以使用ExecutorService接口來提交一個線程.
這里, ThreadPoolExecutor 是實現(xiàn)了 ExecutorService的方法, sumbit的過程就是把一個Runnable接口對象包裝成一個 Callable接口對象, 然后放到 workQueue里等待調(diào)度執(zhí)行. 當然, 執(zhí)行的啟動也是調(diào)用了thread的start來做到的, 只不過這里被包裝掉了. 另外, 這里的thread是會被重復利用的, 所以這里要退出主線程, 需要執(zhí)行以下shutdown方法以示退出使用線程池. 扯遠了.
這種方法是得益于Callable接口和Future模式, 調(diào)用future接口的get方法, 會同步等待該future執(zhí)行結(jié)束, 然后獲取到結(jié)果. Callbale接口的接口方法是 V call(); 是可以有返回結(jié)果的, 而Runnable的 void run(), 是沒有返回結(jié)果的. 所以, 這里即使被包裝成Callbale接口, future.get返回的結(jié)果也是null的.如果需要得到返回結(jié)果, 建議使用Callable接口.
通過隊列來控制線程的進度, 是很好的一個理念. 我們完全可以自己搞個隊列, 自己控制. 這樣也可以實現(xiàn). 不信看代碼:
這里是得益于我們用了一個阻塞隊列, 他的put操作和take操作都會阻塞(同步), 在滿足條件的情況下.當我們調(diào)用take()方法時, 由于子線程還沒結(jié)束, 隊列是空的, 所以這里的take操作會阻塞, 直到子線程結(jié)束的時候, 往隊列里面put了個元素, 表明自己結(jié)束了. 這時候主線程的take()就會返回他拿到的數(shù)據(jù). 當然, 他拿到什么我們是不必去關(guān)心的.
以上幾種情況都是針對子線程只有1個的時候. 當子線程有多個的時候, 情況就不妙了.
第一種方法, 你要調(diào)用很多個線程的join, 特別是當你的線程不是for循環(huán)創(chuàng)建的, 而是一個一個創(chuàng)建的時候.
第二種方法, 要調(diào)用很多的future的get方法, 同第一種方法.
第三種方法, 比較方便一些, 只需要每個線程都在queue里面 put一個元素就好了.但是, 第三種方法, 這個隊列里的對象, 對我們是毫無用處, 我們?yōu)榱耸褂藐犃? 而要不明不白浪費一些內(nèi)存, 那有沒有更好的辦法呢?
有的, concurrency包里面提供了好多有用的東東, 其中, CountDownLanch就是我們要用的.
CountDownLanch 是一個倒數(shù)計數(shù)器, 給一個初始值(>=0), 然后每countDown一次就會減1, 這很符合等待多個子線程結(jié)束的場景: 一個線程結(jié)束的時候, countDown一次, 直到所有都countDown了 , 那么所有子線程就都結(jié)束了.
先看看CountDownLanch有哪些方法:
await: 會阻塞等待計數(shù)器減少到0位置. 帶參數(shù)的await是多了等待時間.
countDown: 將當前的技術(shù)減1
getCount(): 返回當前的計數(shù)
顯而易見, 我們只需要在子線程執(zhí)行之前, 賦予初始化countDownLanch, 并賦予線程數(shù)量為初始值.
每個線程執(zhí)行完畢的時候, 就countDown一下.主線程只需要調(diào)用await方法, 可以等待所有子線程執(zhí)行結(jié)束, 看代碼:
此種方法也適用于使用 ExecutorService summit 的任務的執(zhí)行.
另外還有一個并發(fā)包的類CyclicBarrier, 這個是(子)線程之間的互相等待的利器. 柵欄, 就是把大家都在一個地方堵住, 就像水閘, 等大家都完成了之前的操作, 在一起繼續(xù)下面的操作. 不過就不再本篇的討論范圍內(nèi)了.
EOF
總結(jié)
以上是生活随笔為你收集整理的java 线程执行结束_Java_如何等待子线程执行结束的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java ssh pdf_JavaSSH
- 下一篇: java测试类和类_【测试开发】从测试角