<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:Callable與Runnable的區(qū)別你知道嗎?

          共 3938字,需瀏覽 8分鐘

           ·

          2022-02-15 11:44

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來(lái),我們一起精進(jìn)!你不來(lái),我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!

          編輯:業(yè)余草

          https://juejin.cn/post/7061975326393368583

          推薦:https://www.xttblog.com/?p=5313

          昨天情人節(jié),今天元宵節(jié)。你們都是怎么過(guò)的?

          ?

          JDK版本:JDK11

          ?

          1. 背景

          在平時(shí)的開(kāi)發(fā)過(guò)程中線(xiàn)程肯定用不少,線(xiàn)程啟動(dòng)執(zhí)行需要實(shí)現(xiàn) Runnable 類(lèi):

          public?class?ThreadTest?{

          ????public?static?void?main(String[]?args)?{
          ????????new?Thread(new?Runnable()?{
          ????????????@Override
          ????????????public?void?run()?{
          ????????????????System.out.println(111);
          ????????????}
          ????????},?"Thread-mxsm").start();
          ????}
          }

          是自己新建一個(gè)線(xiàn)程對(duì)象,然后執(zhí)行「Runnable」 執(zhí)行完成線(xiàn)程結(jié)束。

          除了這樣的還有使用到線(xiàn)程池,如下:

          public?class?ThreadTest?{

          ????public?static?void?main(String[]?args)?{
          ???????ExecutorService?executorService?=?Executors.newFixedThreadPool(2);
          ????????executorService.execute(new?Runnable()?{
          ????????????@Override
          ????????????public?void?run()?{
          ????????????????System.out.println(111);
          ????????????}
          ????????});
          ????}
          }

          但是在線(xiàn)程池來(lái)執(zhí)行提交任務(wù)的時(shí)候,你可能注意到了這樣情況如下圖:

          線(xiàn)程池來(lái)執(zhí)行提交任務(wù)

          在標(biāo)號(hào)1的位置,你會(huì)發(fā)現(xiàn)竟然還可以提交一個(gè) 「Callable」 到線(xiàn)程池進(jìn)行執(zhí)行。

          「問(wèn)題來(lái)了:」

          • 「Runnable和Callable有什么關(guān)系,為什么線(xiàn)程池可以提交Callable?!?/strong>
          • 「從單個(gè)線(xiàn)程來(lái)看,線(xiàn)程Thead只能執(zhí)行Runnable接口的實(shí)現(xiàn),但是線(xiàn)程池為什么就可以執(zhí)行Callable」
          • 「Callable如何經(jīng)過(guò)包裝變成Runnable給線(xiàn)程調(diào)用」
          Callable與Runnable

          2. Callable與Runnable的關(guān)聯(lián)分析

          從上面的列出的三個(gè)方面來(lái)分析兩者之間的關(guān)聯(lián)。

          2.1 Callable與Runnable源碼分析

          「Runnable」 平時(shí)在開(kāi)發(fā)的時(shí)候經(jīng)常用,所以大家也比較熟悉:

          @FunctionalInterface
          public?interface?Runnable?{
          ????public?abstract?void?run();
          }

          只有一個(gè)方法 run 。在源碼中的注釋中翻譯總結(jié)一下就是:「任何需要由Thread執(zhí)行的類(lèi)都需要實(shí)現(xiàn)Runnable」

          ?

          Tips: 所以Callable應(yīng)該是巧妙的轉(zhuǎn)換成了Runnable

          ?

          「Callable」 用的不多,看一下源碼:

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

          「Callable」 也只有一個(gè)方法 「call」 但是方法有返回值。

          「從源碼上面可以看出來(lái) Callable 和 Runnable 沒(méi)有任何繼承關(guān)系,Runnable的方法沒(méi)有返回值,而Callable的方法有返回值。」

          2.2 從線(xiàn)程池看Callable如何包裝成Runnable

          JDK在Runnable的注釋上有明確的說(shuō)明:任何需要由Thread執(zhí)行的類(lèi)都需要實(shí)現(xiàn)Runnable。所以我們可以推斷出來(lái)Callable用了某種方式包裝成了Runnable。

          通過(guò) 「ExecutorService#submit」 方法提交 Callable 來(lái)分析:

          1. AbstractExecutorService#submit方法中 newTaskFor 方法將 Callable轉(zhuǎn)換成了 RunnableFuture。
          RunnableFuture

          通過(guò) 「newTaskFor」 方法可以發(fā)現(xiàn),最終是創(chuàng)建了一個(gè) 「FutureTask」 對(duì)象,「Callable」 作為構(gòu)造函數(shù)的參數(shù)。那么看一下「FutureTask」

          public?class?FutureTask<V>?implements?RunnableFuture<V>?{
          ????/**?The?underlying?callable;?nulled?out?after?running?*/
          ????private?Callable?callable;
          ????/**?The?result?to?return?or?exception?to?throw?from?get()?*/
          ????private?Object?outcome;?//?non-volatile,?protected?by?state?reads/writes
          ????/**?The?thread?running?the?callable;?CASed?during?run()?*/
          ????private?volatile?Thread?runner;
          ????/**?Treiber?stack?of?waiting?threads?*/
          ????private?volatile?WaitNode?waiters;
          ????
          ????public?FutureTask(Callable?callable)?{
          ????????if?(callable?==?null)
          ????????????throw?new?NullPointerException();
          ????????this.callable?=?callable;
          ????????this.state?=?NEW;???????//?ensure?visibility?of?callable
          ????}
          ?//省略部分代碼
          }

          Callable作為了FutureTask的一個(gè)屬性值。之前說(shuō)過(guò),要想被Thread執(zhí)行必須實(shí)現(xiàn)Runnable。那么我們看一下 「FutureTask」 的實(shí)現(xiàn)類(lèi) 「RunnableFuture」

          RunnableFuture
          1. 「RunnableFuture」 繼承了 「Runnable」

          換句話(huà)說(shuō):「FutureTask 實(shí)現(xiàn)了 Runnable」

          1. 「FutureTask#run 內(nèi)部就調(diào)用了Callable的call方法」

          然后由線(xiàn)程池中的Thread去執(zhí)行FutureTask(也就是Runnable的實(shí)現(xiàn)的實(shí)例)。上面類(lèi)的繼承關(guān)系:

          「Callable轉(zhuǎn)換成Runnable的流程」

          1. 「開(kāi)發(fā)者實(shí)現(xiàn)Callable接口」
          2. 「實(shí)例化Callable,然后提交到線(xiàn)程池」
          3. 「以Callable為構(gòu)造函數(shù)創(chuàng)建FutureTask」
          4. 「最終將FutureTask提交給線(xiàn)程池的線(xiàn)程進(jìn)行執(zhí)行」
          ?

          Tips: 在提交Runnable的實(shí)現(xiàn)到線(xiàn)程池執(zhí)行的時(shí)候,如果需要獲取到返回值,會(huì)將 Runnable的實(shí)例,通過(guò)RunnableAdapter適配器適配成Callable。

          ?
          ?private?static?final?class?RunnableAdapter<T>?implements?Callable<T>?{
          ?????private?final?Runnable?task;
          ?????private?final?T?result;
          ?????RunnableAdapter(Runnable?task,?T?result)?{
          ?????????this.task?=?task;
          ?????????this.result?=?result;
          ?????}
          ?????public?T?call()?{
          ?????????task.run();
          ?????????return?result;
          ?????}
          ?????public?String?toString()?{
          ?????????return?super.toString()?+?"[Wrapped?task?=?"?+?task?+?"]";
          ?????}
          ?}

          2.3 線(xiàn)程池如何執(zhí)行Callable有什么特點(diǎn)

          將Callable包裝成Runnable后,線(xiàn)程池的執(zhí)行和執(zhí)行Runnable一樣,Callable的特點(diǎn)就是可以獲取到返回值。如果執(zhí)行的邏輯不關(guān)心返回值就可以直接用Runnable來(lái)。但是如果需要涉及到獲取到業(yè)務(wù)邏輯中的返回值那么就使用Callable來(lái)提交到線(xiàn)程池中。

          3. 總結(jié)

          • 「Runnable和Callable兩者沒(méi)有繼承關(guān)系,Callable通過(guò)FutureTask包裝成Runnable。」
          • 「線(xiàn)程池執(zhí)行任務(wù)的時(shí)候,如果關(guān)系返回值就用Callable,不關(guān)心返回值用Runnable?!?/strong>
          • 「Runnable如果也需要返回值,線(xiàn)程池內(nèi)部是通過(guò)RunnableAdapter適配器來(lái)適配成Callable」

          瀏覽 27
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    久爱视颊在线观看 | 麻豆91精品人妻成人无码 | 美女AV操逼 | 啪啪啪啪啪啪AVXXX | 色老板最新地址 |