主線程等待

場景介紹

在實際的工作過程中,為了減少用戶的等待時間,通常會使用多線程去并行處理相關(guān)任務。主任務線程等待其他并行任務處理完成后,獲取執(zhí)行的結(jié)果,經(jīng)過相關(guān)處理,返回給用戶。這種方式是多線程在程序中主要的使用方式,因此這就要求主線程必須等待任務線程的執(zhí)行,然后匯總結(jié)果。

實現(xiàn)方法

join方法

在 CountDownLatch 類未出現(xiàn)之前要實現(xiàn)主線程等待只能使用 join 方法(這樣說有點絕對,因為 Future 的 get 方法也能實現(xiàn)閉鎖的功能,但是需要自己將其緩存后再去循環(huán)處理,不是使用 JDK 所提供的方法了)。join 方法和之前介紹的方法不同,它是線程 Thread 類的方法,它沒有入?yún)⒁矝]有返回值。

示例代碼
public static void main(String[] args) throws InterruptedException {
        System.out.println("main start");
        Thread t1 = new Thread(() -> {
            System.out.println("t1 start");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 end");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2 start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 end");
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("main end");
    }              

如上代碼中,創(chuàng)建了兩個子線程 t1 和 t2, 然后分別啟動兩個子線程并調(diào)用各自的 join 方法。方法執(zhí)行后,會先執(zhí)行兩個子線程,執(zhí)行完后才會執(zhí)行主線程的打印任務。
執(zhí)行過程為,當主線程執(zhí)行到 t1.join() 的時候其會被阻塞,等待 t1 執(zhí)行完后再執(zhí)行 t2.join() 會再次被阻塞,等到 t2 執(zhí)行完后,再執(zhí)行主線程的打印任務。
需要注意的是,要先啟動后再調(diào)用 join 方法才會有用,如果先調(diào)用 join 方法再啟動是不會生效的。

CountDownLatch

上面我們介紹了通過使用線程類的 join 方法來讓主線程等待,但是這個方法不夠靈活,對實際工作的各種場景并不能完全滿足,因此 JDK 提供了 CountDownLatch 類,可以讓我們能更好的實現(xiàn)該功能。

示例代碼
public static void main(String[] args) throws InterruptedException {
        System.out.println("main start");
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(() -> {
            try {
                System.out.println("t1 start");
                Thread.sleep(500);
                System.out.println("t1 end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start();
        new Thread(() -> {
            try {
                System.out.println("t2 start");
                Thread.sleep(1000);
                System.out.println("t2 end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start();
        countDownLatch.await();
        System.out.println("main end");
    }
}

如上代碼中,創(chuàng)建了一個 CountDownLatch 對象,由于有兩個子線程,所以構(gòu)造函數(shù)傳入的參數(shù)為2。然后創(chuàng)建了兩個子線程,在各自的 finally 模塊中調(diào)用 CountDownLatch 對象的 countDown 方法;分別啟動兩個子線程,最后由主線程調(diào)用 CountDownLatch 對象的 await 方法,并執(zhí)行打印任務。
代碼的執(zhí)行過程為,在創(chuàng)建 CountDownLatch 對象時,構(gòu)造方法傳入了計數(shù)器數(shù)量2,主線程調(diào)用 CountDownLatch 對象的 await 方法時將會被阻塞,直到 CountDownLatch 對象中的計數(shù)器變?yōu)?,主線程才會返回,阻塞也就結(jié)束了。在子線程執(zhí)行時,由于調(diào)用了 CountDownLatch 對象的 countDown 方法,每次調(diào)用計數(shù)器都會減1 ,兩次調(diào)用后計數(shù)器變?yōu)?,主線程結(jié)束阻塞打印日志。
需要注意的是,CountDownLatch 對象的 await 方法被返回時,只需要 CountDownLatch 對象的計數(shù)器變?yōu)?即可,所以只需要調(diào)用 countDown 方法的次數(shù)為計數(shù)器的數(shù)量即可,并不需要各線程都執(zhí)行完成,示例代碼如下:

public static void main(String[] args) throws InterruptedException {
        System.out.println("main start");
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(() -> {
            try {
                System.out.println("t1 start");
                Thread.sleep(2000);
                System.out.println("t1 end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start();
        new Thread(() -> {
            try {
                System.out.println("t2 start");
                Thread.sleep(1000);
                System.out.println("t2 end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
                countDownLatch.countDown();
            }
        }).start();
        countDownLatch.await();
        System.out.println("main end");
    }

總結(jié)

1、join 方法是 Thread 類的方法,調(diào)用它時主線程會阻塞到調(diào)用它的線程執(zhí)行完后才執(zhí)行。
2、CountDownLatch 類是 JDK 專門提供的一個用來操作線程等待的類,它是使用計數(shù)器的方法,并不關(guān)心各線程是否執(zhí)行完成,因此它更加的靈活。
3、由于在實際工作中,常常使用線程池來管理程序中的線程,因此使用 CountDownLatch 類來處理會更加靈活實用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。