直播app開發中,關於執行緒需要了解的一些事

雲豹科技程式設計師發表於2021-12-17

一、執行緒 D 在A、B、C都同步執行完畢後執行

我們在直播app開發中要實現的目標是:A、B、C三個執行緒可以同時開始執行,各自獨立執行完成後通知D;D 不會開始執行,直到 A、B 和 C 都執行完畢。所以我們 CountdownLatch 用來實現這種型別的通訊。它的基本用法是:
1、在直播app開發中建立一個計數器,並設定一個初始值, CountdownLatch countDownLatch = new CountDownLatch(3); 2、呼叫countDownLatch.await()進入等待狀態,直到計數值變為0; 3、在其他執行緒呼叫countDownLatch.countDown(),該方法會將計數值減一; 4、當計數器的值變為 0 時,countDownLatch.await()等待執行緒中的方法會繼續執行下面的程式碼。
實現程式碼如下:
public static void runDAfterABC() {
    int count = 3;
    CountDownLatch countDownLatch = new CountDownLatch(count);
    new Thread(() -> {
        System.out.println("INFO: D 等待 A B C 執行完成");
        try {
            countDownLatch.await();
            System.out.println("INFO: A B C 執行完成,D 開始執行");
            System.out.println("D is working");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    for (char threadName = 'A'; threadName <= 'C' ; threadName++) {
        final String name = String.valueOf(threadName);
        new Thread(() -> {
            System.out.println(name + " is working");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + " finished");
            countDownLatch.countDown();
        }).start();
    }
}
得到的結果如下:
INFO: D 等待 A B C 執行完成
A is working
B is working
C is working
C finished
B finished
A finished
INFO: A B C 執行完成,D 開始執行
D is working
其實CountDownLatch它本身就是一個倒數計數器,我們把初始的count值設定為3。D執行的時候,首先呼叫該countDownLatch.await()方法檢查計數器的值是否為0,如果不是0則保持等待狀態. A、B、C 執行完畢後,分別使用countDownLatch.countDown()方法將倒數計數器減1。計數器將減為 0,然後通知await()方法結束等待,D開始繼續執行。
因此,CountDownLatch適用於直播app開發中一個執行緒需要等待多個執行緒的情況。

二、三個執行緒分開準備同時開跑

這一次,A、B、C這三個執行緒都需要分別準備,等三個執行緒都準備好後開始同時執行,我們應該如何在直播app開發中做到這一點?
CountDownLatch可以用來計數,但完成計數的時候,只有一個執行緒的一個await()方法會得到響應,所以多執行緒不能在同一時間被觸發。為了達到執行緒相互等待的效果,我們可以使用該CyclicBarrier,其基本用法為:
1、首先在直播app開發中建立一個公共物件CyclicBarrier,並設定同時等待的執行緒數,CyclicBarrier cyclicBarrier = new CyclicBarrier(3); 2、這些執行緒同時開始準備,準備好後,需要等待別人準備好,所以呼叫cyclicBarrier.await()方法等待別人; 3、當指定的需要同時等待的執行緒都呼叫了該cyclicBarrier.await()方法時,意味著這些執行緒準備好了,那麼這些執行緒就會開始同時繼續執行。
想象一下有三個跑步者需要同時開始跑步,所以他們需要等待其他人都準備好,實現程式碼如下:
public static void runABCWhenAllReady() {
    int count = 3;
    CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
    Random random = new Random();
    for (char threadName = 'A'; threadName <= 'C' ; threadName++) {
        final String name = String.valueOf(threadName);
        new Thread(() -> {
            int prepareTime = random.nextInt(10000);
            System.out.println(name + " 準備時間:" + prepareTime);
            try {
                Thread.sleep(prepareTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + " 準備好了,等待其他人");
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(name + " 開始跑步");
        }).start();
    }
}
得到結果如下:
A 準備時間:1085
B 準備時間:7729
C 準備時間:8444
A 準備好了,等待其他人
B 準備好了,等待其他人
C 準備好了,等待其他人
C 開始跑步
A 開始跑步
B 開始跑步
CyclicBarrier 的作用就是等待多個執行緒同時執行。

三、子執行緒將結果返回給主執行緒

在直播app開發中,往往我們需要建立子執行緒來做一些耗時的任務,然後將執行結果傳回主執行緒。那麼如何在 Java 中實現呢?
一般在建立執行緒的時候,我們會把 Runnable 物件傳遞給 Thread 執行,Runable 的原始碼如下:
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
可以看到 Runable 是一個函式式介面,該介面中的 run 方法沒有返回值,那麼如果要返回結果,可以使用另一個類似的介面 Callable。
函式式介面:只有一個方法的介面 Callable 介面的原始碼如下:
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
可以看出,最大的區別Callable在於它返回的是泛型。
那麼接下來的問題是,如何將直播app開發中的子執行緒的結果傳回去呢?Java 有一個類,FutureTask,它可以與 一起工作Callable,但請注意,get用於獲取結果的方法會阻塞主執行緒。FutureTask 本質上還是一個 Runnable,所以可以直接傳到 Thread 中。
直播app開發中,關於執行緒需要了解的一些事
比如我們想讓子執行緒計算1到100的總和,並將結果返回給主執行緒,程式碼如下:
public static void getResultInWorker() {
    Callable<Integer> callable = () -> {
        System.out.println("子任務開始執行");
        Thread.sleep(1000);
        int result = 0;
        for (int i = 0; i <= 100; i++) {
            result += i;
        }
        System.out.println("子任務執行完成並返回結果");
        return result;
    };
    FutureTask<Integer> futureTask = new FutureTask<>(callable);
    new Thread(futureTask).start();
    try {
        System.out.println("開始執行 futureTask.get()");
        Integer result = futureTask.get();
        System.out.println("執行的結果:" + result);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}
得到的結果如下:
開始執行 futureTask.get()子任務開始執行
子任務執行完成並返回結果
執行的結果:5050
可以看出在主執行緒呼叫futureTask.get()方法時阻塞了主執行緒;然後Callable開始在內部執行並返回操作的結果;然後futureTask.get()得到結果,直播app開發主執行緒恢復執行。
在這裡我們可以瞭解到,FutureTask和Callable可以直接在主執行緒中獲取子執行緒的結果,但是它們會阻塞主執行緒。當然,如果你不希望直播app開發阻塞主執行緒,可以考慮使用ExecutorService把FutureTask到執行緒池來管理執行。
本文轉載自網路,轉載僅為分享乾貨知識,如有侵權歡迎聯絡雲豹科技進行刪除處理 原文連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69996194/viewspace-2848356/,如需轉載,請註明出處,否則將追究法律責任。

相關文章