成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java ExecutorService線程池中的小坑——關(guān)于線程池中拋出的異常處理

wow_worktile / 2642人閱讀

摘要:先看寫的簡(jiǎn)略的代碼線程池中發(fā)現(xiàn)異常,被中斷線程池中發(fā)現(xiàn)異常,被中斷我這是一個(gè)訂單處理流程,主要用到了一個(gè)方法,就是。好了,以上就是對(duì)線程池異常捕捉的一個(gè)記錄。

開發(fā)自己的項(xiàng)目有一段時(shí)間了,因?yàn)槭莻€(gè)長(zhǎng)時(shí)間跑的服務(wù)器端程序,所以異常處理顯得尤為重要。
對(duì)于異常的抓取和日志(狹義上的日志)的分析一點(diǎn)都不能落下。

我們使用了Java自帶的Executor模塊,我只是稍微看了下Executors當(dāng)中三個(gè)線程池的實(shí)現(xiàn)(策略為:Fixed, Cached, Schedule),其實(shí)光看名字就可以了解各自的一些策略信息。OK,這一次我需要一種策略合并Fixed和Cached的兩種特點(diǎn)的自定義Executor。其實(shí)很簡(jiǎn)單,給Cached設(shè)置一個(gè)上線就是了。注意他們的同步隊(duì)列使用的不同,用LinkedBlockingQueue是個(gè)不錯(cuò)的選擇,至于BlockingQueue的實(shí)現(xiàn)可以自行谷歌(以后再記吧)。

先看寫的簡(jiǎn)略的代碼

package com.zjseek.recharge.core;

import com.zjseek.recharge.exception.SKErrorCode;
import com.zjseek.recharge.exception.SKOrderState;
import com.zjseek.recharge.model.OrderModel;
import com.zjseek.recharge.service.OrderService;
import org.apache.log4j.Logger;

import java.sql.Timestamp;
import java.util.concurrent.*;

/**
 * Created by geminiwen on 14-6-28.
 */
public class OrderExceptionThreadExecutor extends ThreadPoolExecutor {
    private Logger logger = Logger.getLogger(OrderExceptionThreadExecutor.class);
    private OrderService orderService;

    public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        init();
    }

    public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        init();
    }

    public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        init();
    }

    public OrderExceptionThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        init();
    }

    private void init() {
        this.orderService = new OrderService();
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        Future f = (Future) r;
        try {
            f.get();
        } catch (InterruptedException e) {
            logger.error("線程池中發(fā)現(xiàn)異常,被中斷", e);
        } catch (ExecutionException e) {
            logger.error("線程池中發(fā)現(xiàn)異常,被中斷", e);
        }

    }
}

我這是一個(gè)訂單處理流程,主要用到了一個(gè)protected方法,就是afterExecute。一看這個(gè)函數(shù)的樣子,想當(dāng)然的以為如果線程池中出了問題,異常自然回在第二個(gè)參數(shù)t中傳過來。
也許的確是這樣的,但是這里有一個(gè)區(qū)別。
我們知道ExecutorServcie中執(zhí)行一個(gè)Runnable有兩個(gè)方法,兩個(gè)分別是

public void execute(Runnable command);
public  Future submit(Runnable task, T result);

別看接受的參數(shù)差不多,其實(shí)submit最后是調(diào)用的execute的,而且在調(diào)用execute前,對(duì)task進(jìn)行了一次封裝,變成了RunnableFuture(它是接口,繼承了RunnableFuture實(shí)際是一個(gè)實(shí)現(xiàn)類FutureTask)。

OK,對(duì)于實(shí)際操作Runnable的不同,暫時(shí)說到這,看下execute方法做了什么事
execute方法對(duì)進(jìn)來的Runnable又包裝成了worker然后進(jìn)入runWorker
runWorker方法中有這么幾行

try {
    beforeExecute(wt, task);
    Throwable thrown = null;
    try {
       task.run();
    } catch (RuntimeException x) {
       thrown = x; throw x;
    } catch (Error x) {
       thrown = x; throw x;
    } catch (Throwable x) {
       thrown = x; throw new Error(x);
    } finally {
       afterExecute(task, thrown);
    }
} finally {
    task = null;
    w.completedTasks++;
    w.unlock();
}

好了,到了最關(guān)鍵的afterExecute這個(gè)步驟,我滿心以為這里所有的異常都會(huì)通過thrown傳遞進(jìn)來,看來我還是太年輕了,之前我們分析過,這個(gè)Runnable已經(jīng)被submit封裝成了FutureTask,那么這個(gè)task.run()除了我們自己定義的run任務(wù)之外,到底還干了啥呢?

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

OK,這段源碼摘自FutureTask中的run方法,實(shí)際我們自己定義的任務(wù)已經(jīng)變成了Callable:

   public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

從它的構(gòu)造函數(shù)就可以看出來。

然后我們?cè)谏厦鎸?shí)際運(yùn)行task的地方其實(shí)是c.call()這一句。

  

result = c.call();

我們寫的任務(wù)全部在這句代碼里面執(zhí)行完畢了,看看外面都wrap了啥? OK 我們所有的Throwable全部已經(jīng)被setException吃掉了,怎么還會(huì)拋出到外面那層的execute中呢?
所以我之前實(shí)驗(yàn)的時(shí)候,在submit中提交任務(wù)無論任務(wù)怎么拋異常,在afterExecute中的第二個(gè)參數(shù)是取不到的,原因就在這。

再回頭看看針對(duì)submit改造的函數(shù)

protected void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    Future f = (Future) r;
    try {
        f.get();
    } catch (InterruptedException e) {
        logger.error("線程池中發(fā)現(xiàn)異常,被中斷", e);
    } catch (ExecutionException e) {
        logger.error("線程池中發(fā)現(xiàn)異常,被中斷", e);
    }

}

當(dāng)然,這里已經(jīng)默認(rèn)r是實(shí)現(xiàn)Future接口了。通過FutureTask的get方法,能把剛剛setException中的異常給拋出來,這樣我們就能真的拿到這些異常了。

結(jié)論

如果我們關(guān)心線程池執(zhí)行的結(jié)果,則需要使用submit來提交task,那么在afterExecute中對(duì)異常的處理也需要通過Future接口調(diào)用get方法去取結(jié)果,才能拿到異常,如果我們不關(guān)心這個(gè)任務(wù)的結(jié)果,可以直接使用ExecutorService中的execute方法(實(shí)際是繼承Executor接口)來直接去執(zhí)行任務(wù),這樣的話,我們的Runnable沒有經(jīng)過多余的封裝,在runWorker中得到的異常也直接能在afterExecute中捕捉。

好了,以上就是對(duì)線程池異常捕捉的一個(gè)記錄。想想應(yīng)該不難,今天也是偶然機(jī)會(huì)看到的。今天在開發(fā)中碰到PHP鎖的問題,頭疼死了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64138.html

相關(guān)文章

  • Java 并發(fā)學(xué)習(xí)筆記

    摘要:方法可以將當(dāng)前線程放入等待集合中,并釋放當(dāng)前線程持有的鎖。此后,該線程不會(huì)接收到的調(diào)度,并進(jìn)入休眠狀態(tài)。該線程會(huì)喚醒,并嘗試恢復(fù)之前的狀態(tài)。 并發(fā) 最近重新復(fù)習(xí)了一邊并發(fā)的知識(shí),發(fā)現(xiàn)自己之前對(duì)于并發(fā)的了解只是皮毛。這里總結(jié)以下Java并發(fā)需要掌握的點(diǎn)。 使用并發(fā)的一個(gè)重要原因是提高執(zhí)行效率。由于I/O等情況阻塞,單個(gè)任務(wù)并不能充分利用CPU時(shí)間。所以在單處理器的機(jī)器上也應(yīng)該使用并發(fā)。為...

    DrizzleX 評(píng)論0 收藏0
  • 任務(wù)異常導(dǎo)致線程中的線程變?yōu)閣aiting狀態(tài)

    摘要:通過搜索引擎了解到以下觀點(diǎn)提交到線程池的任務(wù)如果拋出異常會(huì)導(dǎo)致線程掛掉,遂將提交到線程池的任務(wù)中可能出現(xiàn)的異常進(jìn)行了處理,確實(shí)解決了問題。 背景 項(xiàng)目中存在一些定時(shí)任務(wù)來更新數(shù)據(jù)庫表,借助了線程池提供的一些能力,線上環(huán)境偶爾會(huì)出現(xiàn)網(wǎng)絡(luò)波動(dòng)導(dǎo)致服務(wù)實(shí)例無法連上數(shù)據(jù)庫,只要出現(xiàn)了這種情況,就會(huì)導(dǎo)致數(shù)據(jù)不會(huì)再被更新,通過一些命令發(fā)現(xiàn)更新數(shù)據(jù)庫的線程池中的所有線程都處于waiting狀態(tài)。通過...

    fyber 評(píng)論0 收藏0
  • Java線程池簡(jiǎn)單總結(jié)

    摘要:本文主要內(nèi)容為簡(jiǎn)單總結(jié)中線程池的相關(guān)信息。方法簇方法簇用于創(chuàng)建固定線程數(shù)的線程池。三種常見線程池的對(duì)比上文總結(jié)了工具類創(chuàng)建常見線程池的方法,現(xiàn)對(duì)三種線程池區(qū)別進(jìn)行比較。 概述 線程可認(rèn)為是操作系統(tǒng)可調(diào)度的最小的程序執(zhí)行序列,一般作為進(jìn)程的組成部分,同一進(jìn)程中多個(gè)線程可共享該進(jìn)程的資源(如內(nèi)存等)。在單核處理器架構(gòu)下,操作系統(tǒng)一般使用分時(shí)的方式實(shí)現(xiàn)多線程;在多核處理器架構(gòu)下,多個(gè)線程能夠...

    CoorChice 評(píng)論0 收藏0
  • 美團(tuán)面試題:Java-線程池 ThreadPool 專題詳解

    摘要:去美團(tuán)面試,問到了什么是線程池,如何使用,為什么要用以下做個(gè)總結(jié)。二線程池線程池的作用線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。真正的線程池接口是。創(chuàng)建固定大小的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 去美團(tuán)面試,問到了什么是線程池,如何使用,為什么要用,以下做個(gè)總結(jié)。關(guān)于線程之前也寫過一篇文章《高級(jí)面試題總結(jié)—線程池還能這么玩?》 1、什么是線程池:? java.util...

    enrecul101 評(píng)論0 收藏0
  • 美團(tuán)面試題:Java-線程池 ThreadPool 專題詳解

    摘要:去美團(tuán)面試,問到了什么是線程池,如何使用,為什么要用以下做個(gè)總結(jié)。二線程池線程池的作用線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。真正的線程池接口是。創(chuàng)建固定大小的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 去美團(tuán)面試,問到了什么是線程池,如何使用,為什么要用,以下做個(gè)總結(jié)。關(guān)于線程之前也寫過一篇文章《高級(jí)面試題總結(jié)—線程池還能這么玩?》 1、什么是線程池:? java.util...

    wujl596 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<