最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

FutureTask為何單個(gè)任務(wù)僅執(zhí)行一次原理解析

 更新時(shí)間:2023年11月15日 09:42:21   作者:zhongh?Jim  
這篇文章主要為大家介紹了FutureTask為何單個(gè)任務(wù)僅執(zhí)行一次原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

前幾天會(huì)員領(lǐng)取情況查詢(xún)的接口SQL查詢(xún)超時(shí)出故障了,因?yàn)橛袀€(gè)用戶(hù)買(mǎi)的會(huì)員有點(diǎn)多(哈哈),其實(shí)是 數(shù)據(jù)量大 + 祖?zhèn)鞔a邏輯冗長(zhǎng)

嘗試的解決方案:

SQL:檢查了一下,單個(gè)SQL的耗時(shí)其實(shí)不算大,也能接受,不需要改動(dòng),主要原因是后端邏輯冗長(zhǎng)

FutureTask獲取線程的執(zhí)行結(jié)果:將1次大查詢(xún)劃分為多次小查詢(xún)同時(shí)進(jìn)行,提高接口響應(yīng)速度。且一個(gè)FutureTask僅執(zhí)行一次,不會(huì)出現(xiàn)重復(fù)的查詢(xún)

經(jīng)過(guò)權(quán)衡,我們選擇了后者

一、FutureTask用法

解決方案要用到線程池搭配FutureTask,這里我們就不用了,簡(jiǎn)化點(diǎn)

public class Test {
    //計(jì)算結(jié)果
    int count=0;
    @Test
    public void test(){
        try{  
            FutureTask<Integer> futureTask=new FutureTask<>(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return 1;
                }
            });
            //把FutureTask放入線程中,線程會(huì)運(yùn)行FutureTask的run()代碼塊
            Thread t1=new Thread(futureTask);
            t1.start();
            //獲取計(jì)算的結(jié)果,是一個(gè)阻塞等待返回的方法
            count+=futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //最后結(jié)果: 1
        System.out.println(count);    
    }
}

這里用了構(gòu)造方法public FutureTask(Callable<V> callable)讓FutureTask持有Callable接口的實(shí)例

用到try-catch是由于futureTask.get()方法是一個(gè)阻塞等待的過(guò)程,途中如果被中斷會(huì)拋中斷異常,別的異常都會(huì)以ExecutionException執(zhí)行異常的形式拋出

二、(重要)FutureTask的任務(wù)僅執(zhí)行一次,為何?

FutureTask的run()代碼塊僅執(zhí)行一次!請(qǐng)看注釋

/**
    執(zhí)行結(jié)果(全局變量), 有2種情況:
    1. 順利完成返回的結(jié)果
    2. 執(zhí)行run()代碼塊過(guò)程中拋出的異常
*/
private Object outcome; 
//正在執(zhí)行run()的線程, 內(nèi)存可被其他線程可見(jiàn)
private volatile Thread runner;
???????public void run() {
    /**
        FutureTask的run()僅執(zhí)行一次的原因:
        1. state != NEW表示任務(wù)正在被執(zhí)行或已經(jīng)完成, 直接return
        2. 若state==NEW, 則嘗試CAS將當(dāng)前線程 設(shè)置為執(zhí)行run()的線程,如果失敗,說(shuō)明已經(jīng)有其他線程 先行一步執(zhí)行了run(),則當(dāng)前線程return退出
    */
    if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))
        return;
    try {
        //持有Callable的實(shí)例,后續(xù)會(huì)執(zhí)行該實(shí)例的call()方法
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            }catch (Throwable ex) {
                result = null;
                ran = false;
                //執(zhí)行中拋的異常會(huì)放入outcome中保存
                setException(ex);
            }
            if (ran)
                //若無(wú)異常, 順利完成的執(zhí)行結(jié)果會(huì)放入outcome保存
                set(result);
        }
    }finally {
        // help GC 
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

執(zhí)行run()的代碼塊之后,其他線程如何拿到FutureTask的執(zhí)行結(jié)果?下面的get()方法可以做到

三、get()獲取結(jié)果

public V get() throws InterruptedException, ExecutionException {    
       int s = state;
       //COMPLETING: 正在完成的狀態(tài);  s <= COMPLETING就是未完成
    if (s <= COMPLETING)
        //不計(jì)時(shí)等待,結(jié)束等待的條件只有【完成】、【被中斷】、【被取消】、【拋其他異常(不包括中斷異常、取消異常)】
        s = awaitDone(false, 0L);
    return report(s);    
}

這里提一下線程執(zhí)行的狀態(tài) :

private volatile int state;
//線程創(chuàng)建狀態(tài)
private static final int NEW = 0;
//完成(**一個(gè)瞬時(shí)的標(biāo)記**)   
private static final int COMPLETING = 1;
//正常完成狀態(tài)
private static final int NORMAL = 2;
//執(zhí)行過(guò)程出現(xiàn)異常
private static final int EXCEPTIONAL = 3;
//執(zhí)行過(guò)程中被取消
private static final int CANCELLED = 4;
//線程執(zhí)行被中斷(**一個(gè)瞬時(shí)的標(biāo)記**)
private static final int INTERRUPTING = 5;
//線程執(zhí)行被中斷的狀態(tài)
private static final int INTERRUPTED = 6;

volatile保證了線程執(zhí)行的狀態(tài)改變之后會(huì)刷新到內(nèi)存中,被其他線程可見(jiàn)

如果線程還處于未完成的狀態(tài),即s <= COMPLETING,就會(huì)進(jìn)入等待狀態(tài),調(diào)用awaitDone(false, 0L)方法

get為何阻塞等待?

/**
    @param timed 若是true則為定時(shí)等待,超時(shí)后會(huì)結(jié)束等待,并返回當(dāng)前狀態(tài)state
    @param nanos 如果是定時(shí)等待即第一個(gè)入?yún)imed=true的話,會(huì)設(shè)置對(duì)應(yīng)的等待時(shí)長(zhǎng)
*/
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
    //等待的最后期限
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    //進(jìn)入無(wú)限循環(huán)的等待狀態(tài),只有【完成】、【被取消】、【異?!?、【中斷】、【超時(shí)】這五種情況才會(huì)結(jié)束等待
    for (;;) {
        if (Thread.interrupted()) {
            //線程執(zhí)行被中斷,則移除等待結(jié)點(diǎn)并拋出異常
            removeWaiter(q);
            throw new InterruptedException();
        }
         int s = state;
        //【完成】、【被取消】、【拋其他異?!康臓顟B(tài)都會(huì) 在這 結(jié)束等待
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        //子線程處于任務(wù)完成的瞬時(shí)狀態(tài),要等一會(huì)才能拿到執(zhí)行結(jié)果
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        else if (q == null)
            q = new WaitNode();
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
        else if (timed) {
            //設(shè)置定時(shí)等待并且已經(jīng)超時(shí)了
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else
            LockSupport.park(this);       
    }
}

詳細(xì)的注釋在代碼中,請(qǐng)耐心看一下。

簡(jiǎn)單來(lái)說(shuō),能結(jié)束等待的條件只有5個(gè):

  • 完成
  • 被中斷
  • 設(shè)置定時(shí)等待并超時(shí)
  • 被取消
  • 拋了其他異常,比如RuntimeException,這里的其他異常既不是中斷異常,也不是取消異常

調(diào)用futureTask.get()的等待方式有2種,分為定時(shí)等待和 不計(jì)時(shí)等待:

  • timed=true是定時(shí)等待,會(huì)創(chuàng)建等待結(jié)點(diǎn)q = new WaitNode();并放在棧頂(隊(duì)列頭部),然后掛起。結(jié)束等待的條件(滿(mǎn)足任一即可)是【完成】、【被中斷】、【被取消】、【拋其他異?!?、【超時(shí)】 。
  • timed=false是不計(jì)時(shí)等待,創(chuàng)建等待結(jié)點(diǎn)后會(huì)一直掛起,只有【完成】、【被中斷】、【被取消】、【拋其他異?!?/li>

在等待結(jié)束之前,LockSupport.park(this);表示線程會(huì)被一直掛起,不再繼續(xù)無(wú)限循環(huán)占用CPU。

解除掛起的條件是state > COMPLETING,然后調(diào)用finishCompletion()方法去讓線程解除掛起并回到awaitDone()做最后一次循環(huán)后return state

從get中返回結(jié)果report(int s)

/*正常的計(jì)算結(jié)果 or 拋出的異常 都會(huì)作為outcome*/
private Object outcome;
private V report(int s) throws ExecutionException {
    Object x = outcome;
    //正常完成
    if (s == NORMAL)
        return (V)x;
    //執(zhí)行的過(guò)程中【被取消】
    if (s >= CANCELLED)
        throw new CancellationException();
    /**
        這里拋的是執(zhí)行過(guò)程中發(fā)生的其他異常,既不是【中斷異常】,也不是【被取消異?!?
        比如發(fā)生了RuntimeException之類(lèi)的就會(huì)在這拋
    */    
    throw new ExecutionException((Throwable)x);
}

report(int s)是執(zhí)行g(shù)et()獲取結(jié)果的最后一步

看到這可能有朋友暈了,我把get()內(nèi)部的流程梳理一下:

若要等待計(jì)算結(jié)果:get() -> awaitDone() -> report(),共3步
不用等待:get() -> report() ,僅2步

四、FutureTask是如何拿到線程執(zhí)行的結(jié)果?

主要 有賴(lài)于FutureTask類(lèi)內(nèi)部的Callable接口

只有Callable接口能拿到線程的返回值,下面來(lái)看下FutureTask的構(gòu)造函數(shù)

public class FutureTask<V> implements RunnableFuture<V> {
    //執(zhí)行任務(wù)并返回結(jié)果
    private Callable<V> callable;
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        //新建狀態(tài)
        this.state = NEW;
    }
}

其實(shí)Callable 接口是沒(méi)法 作為創(chuàng)建線程new Thread(Runnable target)的入?yún)⒌?,只有借助FutureTask類(lèi)才能被線程執(zhí)行,因?yàn)镕utureTask實(shí)現(xiàn)了Runnable 接口

有興趣的可以看一下Future接口的關(guān)系圖(這里拿了大佬的圖,侵刪)

FutureTask類(lèi)最終實(shí)現(xiàn)了Future接口和Runnable接口,可作為new Thread(Runnable target)的入?yún)arget來(lái)創(chuàng)建線程

五、FutureTask可能的執(zhí)行過(guò)程

順利完成 :NEW -> COMPLETING -> NORMAL ,即新建->正在完成 ->正常

NEW -> COMPLETING -> EXCEPTIONAL, 執(zhí)行過(guò)程出現(xiàn)了異常

被取消:NEW -> CANCELLED

NEW -> INTERRUPTING -> INTERRUPTED,新建 ->正在被中斷 ->中斷完成

六、列舉一下FutureTask的特性和應(yīng)用場(chǎng)景

特性:

  • 異步執(zhí)行,可執(zhí)行多次(通過(guò)runAndReset()方法),也可僅執(zhí)行一次(執(zhí)行run()即可)
  • 可獲取線程執(zhí)行結(jié)果

應(yīng)用場(chǎng)景:

  • 長(zhǎng)時(shí)間運(yùn)行的任務(wù),包含遠(yuǎn)程調(diào)用的任務(wù)
  • 數(shù)據(jù)量大的查詢(xún),劃分為多個(gè)小查詢(xún),每個(gè)FutureTask 僅執(zhí)行一次 的特性能有效避免重復(fù)的查詢(xún)
  • 計(jì)算密集型的任務(wù)

以上就是FutureTask為何單個(gè)任務(wù)僅執(zhí)行一次原理解析的詳細(xì)內(nèi)容,更多關(guān)于FutureTask單任務(wù)執(zhí)行一次的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java內(nèi)存模型JMM與volatile

    Java內(nèi)存模型JMM與volatile

    這篇文章主要介紹了Java內(nèi)存模型JMM與volatile,Java內(nèi)存模型是一種抽象的概念,并不真實(shí)存在,它描述的是一組規(guī)則或規(guī)范,定義了程序中各個(gè)變量的訪問(wèn)方式
    2022-07-07
  • Java使用EasyExcel模版導(dǎo)出詳細(xì)操作教程

    Java使用EasyExcel模版導(dǎo)出詳細(xì)操作教程

    業(yè)務(wù)中經(jīng)常需要按照一個(gè)特定的模板導(dǎo)出特定內(nèi)容,有些單元格還要求特殊的格式,所以下面這篇文章主要給大家介紹了關(guān)于Java使用EasyExcel模版導(dǎo)出的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • Springboot2.x 使用 Log4j2 異步打印日志的實(shí)現(xiàn)

    Springboot2.x 使用 Log4j2 異步打印日志的實(shí)現(xiàn)

    這篇文章主要介紹了Springboot2.x 使用 Log4j2 異步打印日志的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 深入解讀Java代碼組織中的package包結(jié)構(gòu)

    深入解讀Java代碼組織中的package包結(jié)構(gòu)

    這篇文章主要介紹了Java代碼組織中的package包結(jié)構(gòu),是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2016-03-03
  • 全面解析Java中的引用類(lèi)型

    全面解析Java中的引用類(lèi)型

    在Java中對(duì)象以引用來(lái)指向JVM的內(nèi)存區(qū)塊,這里我們總結(jié)了強(qiáng)引用、軟引用、弱引用和假象引用(幽靈引用),下面就具體來(lái)全面解析Java中的引用類(lèi)型:
    2016-05-05
  • SpringBoot集成RabbitMQ和概念介紹

    SpringBoot集成RabbitMQ和概念介紹

    這篇文章主要介紹了SpringBoot集成RabbitMQ和概念介紹,RabbitMQ即一個(gè)消息隊(duì)列,主要是用來(lái)實(shí)現(xiàn)應(yīng)用程序的異步和解耦,同時(shí)也能起到消息緩沖,消息分發(fā)的作用。更多相關(guān)內(nèi)容需要的小伙伴可以參考一下下面文章內(nèi)容
    2022-05-05
  • Spring加載XSD文件發(fā)生錯(cuò)誤的解決方法

    Spring加載XSD文件發(fā)生錯(cuò)誤的解決方法

    這篇文章主要介紹了Spring加載XSD文件發(fā)生錯(cuò)誤的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Java ArrayList與Vector和LinkedList的使用及源碼分析

    Java ArrayList與Vector和LinkedList的使用及源碼分析

    ArrayList、Vector、LinkedList類(lèi)均在java.util包中,均為可伸縮數(shù)組,即可以動(dòng)態(tài)改變長(zhǎng)度的數(shù)組。ArrayList 和 Vector都是基于存儲(chǔ)元素的Object[] array來(lái)實(shí)現(xiàn)的,它們會(huì)在內(nèi)存中開(kāi)辟一塊連續(xù)的內(nèi)存來(lái)存儲(chǔ)
    2022-11-11
  • Spring配置和使用Properties文件的詳細(xì)步驟

    Spring配置和使用Properties文件的詳細(xì)步驟

    在Spring框架中,.properties 文件通常用于存儲(chǔ)配置信息,如數(shù)據(jù)庫(kù)連接、服務(wù)地址、應(yīng)用參數(shù)等,本文給大家介紹了Spring配置和使用Properties文件的詳細(xì)步驟,需要的朋友可以參考下
    2024-05-05
  • maven中自定義MavenArchetype的實(shí)現(xiàn)

    maven中自定義MavenArchetype的實(shí)現(xiàn)

    Maven自身提供了許多Archetype來(lái)方便用戶(hù)創(chuàng)建Project,為了避免在創(chuàng)建project時(shí)重復(fù)的拷貝和修改,我們通過(guò)自定義Archetype來(lái)規(guī)范顯得還蠻有必要,下面就來(lái)介紹一下,感興趣的可以了解一下
    2025-01-01

最新評(píng)論

金昌市| 喀喇沁旗| 西乌珠穆沁旗| 密云县| 崇明县| 石泉县| 花垣县| 永川市| 象山县| 淮南市| 承德县| 百色市| 漠河县| 巩义市| 河南省| 弥渡县| 托里县| 璧山县| 宁陵县| 迭部县| 鲁山县| 阜城县| 伽师县| 蓬安县| 道孚县| 嘉荫县| 余姚市| 镇赉县| 凌源市| 舒兰市| 察哈| 保亭| 阿拉尔市| 锦州市| 共和县| 长乐市| 海门市| 陕西省| 曲阳县| 五峰| 林州市|