成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

故障現(xiàn)場(chǎng) | 這個(gè)死鎖出奇的詭異

開(kāi)發(fā) 前端
手工拆分線程池確實(shí)能解決這個(gè)場(chǎng)景的問(wèn)題,但由于 GlobalExecuteService 服務(wù)已經(jīng)使用很長(zhǎng)時(shí)間,任務(wù)間的關(guān)系錯(cuò)綜復(fù)雜,很難一次性排查并修復(fù)所有問(wèn)題,同時(shí)隨著邏輯的變化未來(lái)仍舊會(huì)出現(xiàn)類似的問(wèn)題。

1. 問(wèn)題&分析

線程池用多了總會(huì)出現(xiàn)些詭異問(wèn)題,特別是當(dāng)任務(wù)間的關(guān)系比較復(fù)雜時(shí),經(jīng)常會(huì)出現(xiàn)讓你想象不到問(wèn)題,比如這次出現(xiàn)的這個(gè)問(wèn)題。

1.1. 案例

突然間,系統(tǒng)出現(xiàn)大量報(bào)警,具體信息如下:

圖片圖片

從拋出的異常可知,提交量較大導(dǎo)致線程池資源被耗盡,從而觸發(fā)了線程池的拒絕策略,直接拋出了 RejectedExecutionException。

開(kāi)始的時(shí)候,小艾認(rèn)為等高峰流量過(guò)去后,系統(tǒng)便能恢復(fù)正常。可出乎意料的是,系統(tǒng)一直沒(méi)有恢復(fù),那么流量已經(jīng)將至個(gè)位數(shù),請(qǐng)求也是 100% 失敗,同時(shí)該節(jié)點(diǎn)的大量后臺(tái)任務(wù)都出現(xiàn)異常。沒(méi)有辦法,為了快速止損,不得已對(duì)異常節(jié)點(diǎn)進(jìn)行重啟,系統(tǒng)隨之恢復(fù)正常,日志輸入如下:

圖片圖片

其他的后臺(tái)任務(wù)也恢復(fù)正常。

驚魂初定的小艾找到出問(wèn)題的代碼如下:

@GetMapping("syncSubmit")
public RestResult<String> syncSubmit(String taskName){
    this.executeService.submit(new ParentTask());
    return RestResult.success("提交成功");
}

class ParentTask implements Callable<Boolean>{

    @Override
    public Boolean call() throws Exception {
        Future<A> aFuture = executeService.submit(new FetchAChildTask());
        doSomeThing(500);
        Future<B> bFuture = executeService.submit(new FetchBChildTask());
        doSomeThing(500);
        C c = buildC(aFuture.get(), bFuture.get());
        Future<Boolean> cFuture = executeService.submit(new SaveCChildTask(c));
        return cFuture.get();
    }
}

代碼的邏輯非常簡(jiǎn)單,核心流程如下圖所示:

圖片圖片

核心流程為:

  1. 提交異步任務(wù),并行獲取 A 和 B
  2. 線程同步處理一下耗時(shí)操作
  3. 獲取 A 和 B 結(jié)果后,構(gòu)建新對(duì)象 C
  4. 將 C 保存到數(shù)據(jù)庫(kù)

邏輯非常簡(jiǎn)單,唯一的復(fù)雜點(diǎn)在于:==多處任務(wù)提交使用了統(tǒng)一線程池。==

【背景】考慮到線程池是系統(tǒng)中最寶貴的資源,公司“大牛”封裝了一個(gè)全局的 GlobalExecuteService 服務(wù),并制定規(guī)范要求所有異步任務(wù)統(tǒng)一使用 GlobalExecuteService 來(lái)完成。如果需要構(gòu)建自己的線程池,需要向他提交審批,只有在審批后才能創(chuàng)建新的線程池。

1.2. 問(wèn)題分析

線程池處于什么狀態(tài)?為什么所有異步任務(wù)都無(wú)法提交?

這是一個(gè)比較燒腦的問(wèn)題,單盤邏輯沒(méi)有什么頭緒,沒(méi)有辦法只能將現(xiàn)場(chǎng) dump 下來(lái)進(jìn)行分析。

第一個(gè)問(wèn)題:線程池線程都處于什么狀態(tài),線程棧信息如下:

圖片圖片

從日志中可知:

  1. 線程池中的線程全部處于 WAITING,也就是等待狀態(tài);
  2. 展開(kāi) Thread-1 棧信息,發(fā)現(xiàn)線程再調(diào)用 future.get 操作時(shí)出現(xiàn)阻塞
  3. 實(shí)際等待對(duì)象為 FutrueTask#10

接下來(lái),需要進(jìn)一步確認(rèn) FutureTask#10 具體處于什么狀態(tài),從內(nèi)存堆中找到 FutureTask#10 對(duì)象,詳細(xì)信息如下:

圖片圖片

從日志中可以看出:

  1. FutureTask#10 是 LinkedBlockingQueue 的 Node 節(jié)點(diǎn)持有,也就是 FutureTask#10 處于等待隊(duì)列中
  2. 該阻塞隊(duì)列屬于 GlobalExecuteService 所有

排查到這里,真相浮出水面:GlobalExecuteService 中的線程,正在等待 GlobalExecuteService 阻塞隊(duì)列的任務(wù)完成。

具體如下圖所示:

圖片圖片

線程池中的所有工作線程都在等待阻塞隊(duì)列的任務(wù)完成,由于沒(méi)有可用的工作線程,阻塞隊(duì)列中的任務(wù)永遠(yuǎn)都不會(huì)被執(zhí)行。

這就是典型的死鎖!!!

2. 解決方案

費(fèi)了老大勁終于定位問(wèn)題,解決思路也就變的明了:不要向自己運(yùn)行的線程池提交任務(wù)。

圖解如下:

圖片圖片

線程池不會(huì)向自己提交任務(wù),而是將任務(wù)提交到其他線程池。

2.1. 手工拆分線程池

問(wèn)題修復(fù)變的簡(jiǎn)單,我們需要:

  1. 先建一個(gè)子線程池服務(wù)
  2. 父線程池向子線程池提交任務(wù)

具體代碼如下:

@Autowired
private GlobalExecuteService executeService;

// 創(chuàng)建新的線程池服務(wù)
@Autowired
private SubExecuteService subExecuteService;

@GetMapping("syncSubmit")
public RestResult<String> syncSubmit(String taskName){
    this.executeService.submit(new ParentTask());
    return RestResult.success("提交成功");
}

class ParentTask implements Callable<Boolean>{

    @Override
    public Boolean call() throws Exception {
        log.info("Begin to Run Parent Task");

        // 向新的線程池服務(wù)提交任務(wù)
        Future<A> aFuture = subExecuteService.submit(new FetchAChildTask());
        doSomeThing(500);

        // 向新的線程池服務(wù)提交任務(wù)
        Future<B> bFuture = subExecuteService.submit(new FetchBChildTask());
        doSomeThing(500);
        C c = buildC(aFuture.get(), bFuture.get());

        // 向新的線程池服務(wù)提交任務(wù)
        Future<Boolean> cFuture = subExecuteService.submit(new SaveCChildTask(c));
        Boolean result = cFuture.get();
        log.info("End to Run Parent Task");
        return result;
    }
}

2.2. 多級(jí)任務(wù)管理

手工拆分線程池確實(shí)能解決這個(gè)場(chǎng)景的問(wèn)題,但由于 GlobalExecuteService 服務(wù)已經(jīng)使用很長(zhǎng)時(shí)間,任務(wù)間的關(guān)系錯(cuò)綜復(fù)雜,很難一次性排查并修復(fù)所有問(wèn)題,同時(shí)隨著邏輯的變化未來(lái)仍舊會(huì)出現(xiàn)類似的問(wèn)題。

那最佳方案是什么?

讓 GlobalExecuteService 具備多級(jí)管理能力。核心代碼如下:

@Service
public class GlobalExecuteServiceV2 {
    // 記錄當(dāng)前線程運(yùn)行級(jí)別,默認(rèn) 0,表示當(dāng)前線程非該類管理的線程池線程
    private static final ThreadLocal<Integer> LEVEL_HOLDER = ThreadLocal.withInitial(()->0);
    // 一級(jí)線程池
    private ExecutorService executorServiceLeve1;
    // 二級(jí)線程池
    private ExecutorService executorServiceLeve2;
    // 默認(rèn)線程池
    private ExecutorService defExecutorService;

    @PostConstruct
    public void init() {
       // 省略線程池初始化邏輯
    }

    public <T> Future<T> submit(Callable<T> callable){
        // 獲取當(dāng)前線程的運(yùn)行級(jí)別
        Integer level = LEVEL_HOLDER.get();
        // 根據(jù)當(dāng)前運(yùn)行級(jí)別,計(jì)算子任務(wù)所使用的線程池
        ExecutorService executorService = getNextExecutorServiceByLevel(level);
        // 為子任務(wù)分配運(yùn)行級(jí)別
        CallableWrapper<T> callableWrapper = new CallableWrapper<>(level + 1, callable);
        // 提交任務(wù)
        return executorService.submit(callableWrapper);
    }

    private ExecutorService getNextExecutorServiceByLevel(Integer level) {
        if (level == 0){
            return executorServiceLeve1;
        }
        if (level == 1){
            return executorServiceLeve2;
        }
        return defExecutorService;
    }

    class CallableWrapper<T> implements Callable<T>{
        private final Integer level;
        private final Callable<T> callable;

        CallableWrapper(Integer level, Callable<T> callable) {
            this.level = level;
            this.callable = callable;
        }

        @Override
        public T call() throws Exception {
            try {
                // 為線程池綁定運(yùn)行級(jí)別
                LEVEL_HOLDER.set(level);
                return callable.call();
            }finally {
                // 清理線程池運(yùn)行級(jí)別
                LEVEL_HOLDER.remove();
            }
        }
    }
}

核心設(shè)計(jì)如下:

  1. 使用 ThreadLocal<Integer> LEVEL_HOLDER 記錄當(dāng)前線程運(yùn)行的級(jí)別,默認(rèn)為 0 表示任務(wù)未在線程池中運(yùn)行
  2. 提交任務(wù)時(shí),通過(guò)當(dāng)前運(yùn)行級(jí)別計(jì)算下一級(jí)別的線程池
  • 當(dāng)前級(jí)別為0,返回 Level1 線程池
  • 當(dāng)前級(jí)別為1,返回 Level2 線程池
  • 其他基本,返回 默認(rèn) 線程池
  1. 通過(guò) CallableWrapper 自動(dòng)將任務(wù)運(yùn)行的 Level 綁定到當(dāng)前線程上下文
  2. 任務(wù)執(zhí)行前,使用 LEVEL_HOLDER.set(level) 完成運(yùn)行 level 的設(shè)置
  3. 任務(wù)執(zhí)行后,使用 LEVEL_HOLDER.remove() 完成運(yùn)行 level 的清理

為了演示方便,僅定義了 3 級(jí)線程池,通常情況下足夠業(yè)務(wù)使用,但需要注意:

  • 超過(guò)三級(jí)提交,仍舊有可以出現(xiàn)死鎖的情況,可以通過(guò)日志方式及時(shí)暴露問(wèn)題
  • 如不放心,可以升級(jí)為 “無(wú)限極” 設(shè)計(jì),及使用 List<ExecutorService> 對(duì)線程池進(jìn)行統(tǒng)一管理,并根據(jù) Level 完成線程池的動(dòng)態(tài)創(chuàng)建

3. 示例&源碼

代碼倉(cāng)庫(kù):

https://gitee.com/litao851025/learnFromBug

代碼地址:

https://gitee.com/litao851025/learnFromBug/tree/master/src/main/java/com/geekhalo/demo/thread/deadlock

責(zé)任編輯:武曉燕 來(lái)源: geekhalo
相關(guān)推薦

2009-08-19 22:29:12

VMWare系統(tǒng)時(shí)間故

2009-06-12 16:55:10

VPN客戶端故障

2010-03-25 12:21:44

無(wú)線網(wǎng)絡(luò)掉線故障

2010-07-30 12:50:28

2009-08-06 10:28:11

Vmware虛擬機(jī)自動(dòng)

2024-04-01 09:46:11

MQ消息亂序

2019-06-19 08:59:52

數(shù)據(jù)庫(kù)死鎖堆棧

2019-04-15 13:15:12

數(shù)據(jù)庫(kù)MySQL死鎖

2009-07-07 17:22:34

光纖鏈路測(cè)試故障

2009-06-11 16:26:49

無(wú)線加密身份驗(yàn)證故障

2024-06-03 10:10:01

2024-03-18 09:24:12

RocketMQ消息模型分布式

2024-03-18 09:10:00

死鎖日志binlog

2020-12-08 09:25:41

死鎖MySQL數(shù)據(jù)庫(kù)

2023-12-11 09:18:33

控制取值范圍Type

2025-01-07 08:20:00

2023-06-14 08:34:18

Mybatis死鎖框架

2020-02-28 14:48:51

結(jié)構(gòu)系統(tǒng)程序

2010-08-24 16:13:38

面試

2024-01-22 09:16:47

多線程性能優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 色婷婷久久| 黄色片网站在线观看 | 91久久久久久久 | www四虎影视| 免费的日批视频 | 91免费观看国产 | 日韩视频中文字幕 | 超碰在线人人 | 狠狠色狠狠色综合系列 | 日韩午夜一区二区三区 | 怡红院怡春院一级毛片 | 国产情侣久久 | 91精产国品一二三区 | 国产精品久久一区 | 免费精品视频一区 | 91在线看| 国产免费va| 国产精品18久久久久久白浆动漫 | 91视频网 | 国产日韩欧美精品一区二区三区 | 国产伦一区二区三区四区 | 91一区二区 | 中文字幕国产第一页 | 中文字幕av一区二区三区 | 久久黄色网 | 国产免费一区二区三区 | 中文字幕 亚洲一区 | 亚洲中国字幕 | 日本色综合| 精品免费av | 在线观看国产视频 | 欧美激情一区二区三区 | 精品视频一区二区三区在线观看 | 亚洲欧美一区二区三区国产精品 | 国产激情视频在线 | 草草视频在线播放 | 一级毛片网 | 国产高清一区二区三区 | 九九精品在线 | 日韩久久久久 | 国产精品亚洲第一区在线暖暖韩国 |