首頁  >  文章  >  Java  >  子執行緒任務全部完成後主執行緒關閉的四種方法

子執行緒任務全部完成後主執行緒關閉的四種方法

坏嘻嘻
坏嘻嘻原創
2018-09-14 16:27:434081瀏覽

多線程是java中很重要的知識點,在此小編給大家總結Java Thread多線程,非常有用,希望大家可以掌握哦。

    • 方法一Thread.sleep

    • #方法二ExecutorService

    • 方法三thread .join

    • 方法四Thread.yield and Thread.activeCount


#寫程式碼過程中遇到了這樣的場景, 需要觀察各個子執行緒運行的情況, 如果不進行任何處理當

main 方法運行完畢後其他子執行緒都會關閉, 無法觀察到所有子線程的詳細的運行情況, 於是需要讓主線程等待所有子線程運行完畢後才關閉, 以前比較馬虎的做法就是在main 函數裡面添加Thread. sleep(time)

但是這種方法始終不完美, 因為需要人為的設置等待時間, 不是最佳的實踐, 於是查閱了一些資料以及博客, 此處總結一下實現這個目的的四種方法.

方法一Thread.sleep

這應該是最常見的方法, 雖然不是最佳的實踐, 但是可以勉強滿足需求.

public static void main(String[] args) throws InterruptedException{        for(int i=0; i<10; i++){            new Thread("subthread" + i){
                @Override                public void run() {                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "finished");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }.start();
        }
        Thread.sleep(2000);
        System.out.println("main thread finished");
    }

運行結果:

subthread0finished
subthread1finished
subthread2finished
subthread6finished
subthread4finished
subthread5finished
subthread3finished
subthread9finished
subthread8finished
subthread7finished
main thread finished

方法二ExecutorService

可以使用執行緒池實作, 常用的執行緒池物件都是

ExecutorService

介面的實作, 其中提供了

shutdown 等方法可以保證目前提交的任務在子執行緒上運行完畢後Java進程正常退出.

public static void main(String[] args) {        // 创建一个ExecutorService
        ExecutorService ex =  Executors.newCachedThreadPool();        for(int i=0; i<10; i++){            // 添加 task
            ex.submit(new Thread("subthread" + i){

                @Override                public void run() {                    try{
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "finished");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
        }        // 使用shutdown 会通知executorservice 停止添加其它task 它会等待所有子线程运行结束才退出Java进程
        ex.shutdown();
        System.out.println("main thread finished");

    }
運行結果為:
main thread finished
pool-1-thread-3finished
pool-1-thread-4finished
pool-1-thread-2finished
pool-1-thread-1finished
pool-1-thread-5finished
pool-1-thread-7finished
pool-1-thread-8finished
pool-1-thread-6finished
pool-1-thread-9finished
pool-1-thread-10finished
此種方法有一些小的瑕疵, 我們從輸出的資訊可以看到主線程其實先於子線程運行完畢, 所以這種方法只能夠保證子線程在程序退出之前可以運行完, 但是不能夠保證主線程在子線程運行完畢之後再執行.所以代碼還需要更改,新增一個awaitTermination(time, timeunit) 設定一個較為合理的等待的時間, 等待子執行緒運行完畢.
public static void main(String[] args) {        // 创建一个ExecutorService
        ExecutorService ex =  Executors.newCachedThreadPool();        for(int i=0; i<10; i++){            // 添加 task
            ex.submit(new Thread("subthread" + i){

                @Override                public void run() {                    try{
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "finished");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
        }        // 使用shutdown 会通知executorservice 停止添加其它task 它会等待所有子线程运行结束才退出Java进程
        ex.shutdown();        try {            // 设置等待时间等待子线程运行完毕
            if(!ex.awaitTermination(2000, TimeUnit.MILLISECONDS)){                // 等待时间内子线程并未全部运行完毕就直接关闭
                ex.shutdownNow();
            }
        }catch(InterruptedException e){
            ex.shutdownNow();
        }

        System.out.println("main thread finished");

    }
運行結果:
pool-1-thread-1finished
pool-1-thread-5finished
pool-1-thread-4finished
pool-1-thread-9finished
pool-1-thread-8finished
pool-1-thread-3finished
pool-1-thread-2finished
pool-1-thread-7finished
pool-1-thread-6finished
pool-1-thread-10finished
main thread finished

可以看到主執行緒執行的內容在最後輸出的. 這個方法與方法一一樣都需要設定等待時間, 不是很完美的方法.

方法三thread.join

thread. join

表示執行這段程式碼的執行緒會處於掛起狀態, 等待呼叫這個方法的執行緒(這裡就是這個thread) 運行完畢後才繼續運行. 下面的例子中子執行緒都是在

main 執行緒上面建立的, 所以在main 執行緒裡面執行某一個子執行緒.join 時會等待子執行緒運行完畢才繼續執行main 執行緒, 程式碼如下:

public static void main(String[] args) throws InterruptedException{
        List<Thread> list = new ArrayList<>();        for(int i=0; i<10; i++){
            Thread t = new Thread("subthread" + i){
                @Override                public void run() {                    try{
                        Thread.sleep(3000);
                        System.out.println(Thread.currentThread().getName() + "finished");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            };
            list.add(t);
            t.start();
        }        for(Thread item : list){
            item.join();
        }
        System.out.println("main thread finished");
    }
執行結果:
subthread1finished
subthread2finished
subthread0finished
subthread3finished
subthread6finished
subthread5finished
subthread4finished
subthread9finished
subthread7finished
subthread8finished
main thread finished

使用這個方法相比去上面兩種方法優點在於不用設定等待時間.

方法四Thread.yield and Thread.activeCount

首先說明一下這兩個方法的作用, Thread.yield

# 通俗講就是讓步, 呼叫這個方法的當前執行緒

放棄

自己佔用

CPU 的權利. 但是並不表示當前線程一定不再執行. Thread.activeCount 方法返回的是當前調用這個線程對應的線程組中所有的活躍線程數量. 在創建線程的時候(new Thread) 其中這個ThreadGroup
參數指的就是創建線程對應的線程組, 如果這個參數沒有指定, 那麼創建的線程與創建這個線程的線程是同一個線程組. 程式碼如下:

public static void main(String[] args) {        // 关键参数
        int defaultThreadNum = 2;        for(int i=0; i<10 ;i++){            new Thread("subthread" + i){
                @Override                public void run() {                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "finished");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }.start();
        }        while(Thread.activeCount() > defaultThreadNum){            // 当活跃线程数大于设定的默认线程数的时候 主线程让步
            Thread.yield();
        }
        System.out.println("main thread finished");
    }

運行結果:

subthread0finished
subthread4finished
subthread1finished
subthread8finished
subthread9finished
subthread5finished
subthread2finished
subthread3finished
subthread6finished
subthread7finished
main thread finished

有一個很關鍵的地方就是這個

defaultthreadnum 設定的是2. 有一些blog中設定的是1, 但是1會導致無限循環, 主線程無法退出, 原因在於大家都認為主線程所在的線程組中排除子線程後只剩下主線程一個線程了, 其實不然, 比如我們運行如下的代碼:

public static void main(String[] args) {
        Thread.currentThread().getThreadGroup().list();
    }

輸出:

java.lang.ThreadGroup[name=main,maxpri=10]    Thread[main,5,main]    Thread[Monitor Ctrl-Break,5,main]

可以看到主線程所在的線程組中還有一個叫

Monitor Ctrl-Break

的線程, 因此排除所有子線程後還剩餘2個線程, 所以循環判斷的門限(defaultThreadNum) 需要設定為2.

此方法也是不需要設定等待時間的.

綜上所述, 如果需要實作主執行緒在所有子執行緒運行結束後再運行可以使用方法三和方法四.

######### ######相關推薦:############Java Thread多執行緒全面解析###########php5 non -thread-safe和thread-safe的差別######

以上是子執行緒任務全部完成後主執行緒關閉的四種方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn