日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

创建线程都有哪些方式?— Callable篇

發(fā)布時間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 创建线程都有哪些方式?— Callable篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今天我們來看一道面試題引發(fā)的思考

問: 創(chuàng)建線程都有哪些方式?

答: 我了解的有四種創(chuàng)建方式:

  • 繼承Thread類創(chuàng)建線程類
  • 通過Runnable接口創(chuàng)建線程類
  • 通過Callable和Future創(chuàng)建線程
  • 通過線程池創(chuàng)建
  • 相信大家回答這個問題沒什么難度吧?通常問完創(chuàng)建方式,那么接下來就是問「1、2」跟「3」創(chuàng)建方式的不同了,只要說出「3」有返回值基本這個問題就過了,不管是出于好奇還是疑惑,我們今天來會會這個Callable。

    實現(xiàn)Runnable接口或者是繼承Thread,這兩種方式都有個缺點,那就是在線程執(zhí)行完之后無法獲得返回值,所謂的返回值在這舉個例子,比如有一個下載功能,下載某個界面的所有圖片,圖片下載完之后要返回給用戶用于展示,如果我們采用方式1、2的話,顯然很難實現(xiàn),所以我們可以采用實現(xiàn)Callbale接口,并用Future接收多線程的執(zhí)行結(jié)果來實現(xiàn),具體代碼如下:

    public class TestCallable {public static void main(String[] args) {DownImgThread demo = new DownImgThread();FutureTask<List<String>> futureTask = new FutureTask<>(demo);new Thread(futureTask).start();try {System.out.println("-----開始獲取圖片-----");List<String> imgs = futureTask.get();for (String img: imgs) {System.out.println("圖片:"+img);}System.out.println("-----獲取圖片結(jié)束-----");} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}/*** 下載圖片的線程DownImgThread*/ public class DownImgThread implements Callable<List<String>> {@Overridepublic List<String> call() throws Exception {List result = new ArrayList();for (int i = 0; i < 10; i++) {result.add("https://sscai.club/img/a"+i+".jpg");}/*** 模擬3秒的網(wǎng)絡(luò)請求操作*/Thread.sleep(3000);return result;} }

    執(zhí)行結(jié)果:

    -----開始獲取圖片----- 圖片:https://sscai.club/img/a0.jpg 圖片:https://sscai.club/img/a1.jpg 圖片:https://sscai.club/img/a2.jpg 圖片:https://sscai.club/img/a3.jpg 圖片:https://sscai.club/img/a4.jpg 圖片:https://sscai.club/img/a5.jpg 圖片:https://sscai.club/img/a6.jpg 圖片:https://sscai.club/img/a7.jpg 圖片:https://sscai.club/img/a8.jpg 圖片:https://sscai.club/img/a9.jpg -----獲取圖片結(jié)束-----

    通過如上代碼我門來看看Callable和Future是如何使用的,首先DownImgThread類(下載圖片類)實現(xiàn)了Callable接口,接口后面攜帶一個泛型T,這個泛型T將用作返回值,我們來看一下 Callable 的接口定義:

    @FunctionalInterface public interface Callable<V> {V call() throws Exception; }

    如上,Callable接口只有一個 call() 方法,同時 call() 方法有一個返回值 V ,所以上邊我們在實現(xiàn) call() 方法時才能將返回值返回回去。在 call() 方法內(nèi)部通過一個「for循環(huán)+休眠」模擬了網(wǎng)絡(luò)請求的過程,然后將圖片的集合返回。

    我們再回到主線程main方法,整體代碼還是比較清晰的,可能小伙伴對FutureTask有點陌生。

    在了解FutureTask之前,我們先來了解一下Future。

    Future

    Future是用來獲取異步計算結(jié)果的,這個結(jié)果是未來的,只有當(dāng)結(jié)果最終處理完成后才會出現(xiàn)在Future中,我們來看一下Future接口的源碼。

    public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException; }

    解讀這5個方法:

    • boolean cancel(boolean mayInterruptIfRunning):如果任務(wù)還沒開始,執(zhí)行cancel(…)方法將返回false;如果任務(wù)已經(jīng)啟動,執(zhí)行cancel(true)方法將以中斷執(zhí)行此任務(wù)線程的方式來試圖停止任務(wù),如果停止成功,返回true;當(dāng)任務(wù)已經(jīng)啟動,執(zhí)行cancel(false)方法將不會對正在執(zhí)行的任務(wù)線程產(chǎn)生影響(讓線程正常執(zhí)行到完成),此時返回false;當(dāng)任務(wù)已經(jīng)完成,執(zhí)行cancel(…)方法將返回false。mayInterruptRunning參數(shù)表示是否中斷執(zhí)行中的線程。
    • boolean isCancelled():如果任務(wù)完成前被取消,則返回true。
    • boolean isDone():如果任務(wù)執(zhí)行結(jié)束,無論是正常結(jié)束或是中途取消還是發(fā)生異常,都返回true。
    • V get():獲取異步執(zhí)行的結(jié)果,如果沒有結(jié)果可用,此方法會阻塞直到異步計算完成。
    • V get(long timeout, TimeUnit unit):獲取異步執(zhí)行結(jié)果,如果沒有結(jié)果可用,此方法會阻塞,但是會有時間限制,如果阻塞時間超過設(shè)定的timeout時間,該方法將拋出異常。

    總之,Future實際上提供了3種功能:

  • 能夠中斷執(zhí)行中的任務(wù);
  • 判斷任務(wù)是否執(zhí)行完成;
  • 獲取任務(wù)執(zhí)行完成后的結(jié)果。
  • 但是我們必須明白Future只是一個接口,我們無法直接創(chuàng)建對象,因此就需要其實現(xiàn)類FutureTask。

    FutureTask

    用FutureTask來實現(xiàn)Future,但是這里并不是直接實現(xiàn)的,我們通過一張圖來看一下之間的關(guān)系:

    FutureTask代碼中對應(yīng)截圖部分:

    public class FutureTask<V> implements RunnableFuture<V> {

    FutureTask實現(xiàn)了RunnableFuture接口,然后我們再來看一下RunnableFuture接口:

    public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }

    RunnableFuture同時實現(xiàn)了Runnable、Future這兩個接口,也就是,既可以通過Runnable接口實現(xiàn)線程,也可以通過Fufure取得異步線程執(zhí)行后的結(jié)果,因為實現(xiàn)了Runnable的緣故,那么FutureTask也可以直接提交給Executor執(zhí)行,Executor接口代碼如下:

    public interface Executor {void execute(Runnable command); }

    這個地方為什么要提到Executor呢?

    我們再回到最上邊寫的那個測試代碼TestCallable中,有這么一行代碼:new Thread(futureTask).start();,這段代碼是用來創(chuàng)建并開啟一個線程,但是問題來了,這種創(chuàng)建線程的方式是有很大弊端的:

    • 每次new Thread新建對象性能差;
    • 線程缺乏統(tǒng)一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統(tǒng)資源導(dǎo)致死機或OOM;
    • 缺乏更多功能,如定時執(zhí)行、定期執(zhí)行、線程中斷。

    而通過Executor線程池的方式顯然要效率很高。

    至此,我們了解了Callable、Future、FutureTask,同時知道了Future跟FutureTask之間的關(guān)系,還間接地了解到FutureTask也可以直接提交給Executor執(zhí)行,那么我們重新修改一下最開始的代碼:

    public class TestCallable {public static void main(String[] args) {/**創(chuàng)建Callable對象任務(wù) **/DownImgThread demo = new DownImgThread();/**創(chuàng)建線程池**/ExecutorService executorService = Executors.newSingleThreadExecutor();/**提交任務(wù)并獲取執(zhí)行結(jié)果**/Future<List<String>> futureTask = executorService.submit(demo);/**關(guān)閉線程池**/executorService.shutdown();try {System.out.println("-----開始獲取圖片-----");List<String> imgs = futureTask.get();for (String img: imgs) {System.out.println("圖片:"+img);}System.out.println("-----獲取圖片結(jié)束-----");} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}

    執(zhí)行結(jié)果:

    -----開始獲取圖片----- 圖片:https://sscai.club/img/a0.jpg 圖片:https://sscai.club/img/a1.jpg 圖片:https://sscai.club/img/a2.jpg 圖片:https://sscai.club/img/a3.jpg 圖片:https://sscai.club/img/a4.jpg 圖片:https://sscai.club/img/a5.jpg 圖片:https://sscai.club/img/a6.jpg 圖片:https://sscai.club/img/a7.jpg 圖片:https://sscai.club/img/a8.jpg 圖片:https://sscai.club/img/a9.jpg -----獲取圖片結(jié)束-----

    眼尖的小伙伴,估計要開始吐槽創(chuàng)建線程池的方式了,是的阿里巴巴開發(fā)規(guī)范中強制要求「線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式」,所以也是給小伙伴留個作業(yè),自己動手修改一下上方代碼吧。

    本文首發(fā)于博客園:https://www.cnblogs.com/niceyoo/p/13380070.html

    博客園關(guān)注一下唄,博客園是我寫文章的主陣地~

    總結(jié)

    以上是生活随笔為你收集整理的创建线程都有哪些方式?— Callable篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。