<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    xylz,imxylz

    關(guān)注后端架構(gòu)、中間件、分布式和并發(fā)編程

       :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks

    Java中線程執(zhí)行的任務(wù)接口java.lang.Runnable 要求不拋出Checked異常,

    public interface Runnable {

        
    public abstract void run();
    }

    那么如果 run() 方法中拋出了RuntimeException,將會怎么處理了?

    通常java.lang.Thread對象運(yùn)行設(shè)置一個(gè)默認(rèn)的異常處理方法:

    java.lang.Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)

    而這個(gè)默認(rèn)的靜態(tài)全局的異常捕獲方法時(shí)輸出堆棧。

    當(dāng)然,我們可以覆蓋此默認(rèn)實(shí)現(xiàn),只需要一個(gè)自定義的java.lang.Thread.UncaughtExceptionHandler接口實(shí)現(xiàn)即可。

    public interface UncaughtExceptionHandler {

        
    void uncaughtException(Thread t, Throwable e);
    }

    而在線程池中卻比較特殊。默認(rèn)情況下,線程池 java.util.concurrent.ThreadPoolExecutor 會Catch住所有異常, 當(dāng)任務(wù)執(zhí)行完成(java.util.concurrent.ExecutorService.submit(Callable))獲取其結(jié)果 時(shí)(java.util.concurrent.Future.get())會拋出此RuntimeException。

    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     * 
    @return the computed result
     * 
    @throws CancellationException if the computation was cancelled
     * 
    @throws ExecutionException if the computation threw an exception
     * 
    @throws InterruptedException if the current thread was interrupted while waiting
     
    */
    V get() 
    throws InterruptedException, ExecutionException;

    其中 ExecutionException 異常即是java.lang.Runnable 或者 java.util.concurrent.Callable 拋出的異常。

    也就是說,線程池在執(zhí)行任務(wù)時(shí)捕獲了所有異常,并將此異常加入結(jié)果中。這樣一來線程池中的所有線程都將無法捕獲到拋出的異常。 從而無法通過設(shè)置線程的默認(rèn)捕獲方法攔截的錯(cuò)誤異常。

    也不同通過自定義線程來完成異常的攔截。

    好在java.util.concurrent.ThreadPoolExecutor 預(yù)留了一個(gè)方法,運(yùn)行在任務(wù)執(zhí)行完畢進(jìn)行擴(kuò)展(當(dāng)然也預(yù)留一個(gè)protected方法beforeExecute(Thread t, Runnable r)):

    protected void afterExecute(Runnable r, Throwable t) { } 

    此方法的默認(rèn)實(shí)現(xiàn)為空,這樣我們就可以通過繼承或者覆蓋ThreadPoolExecutor 來達(dá)到自定義的錯(cuò)誤處理。

    解決辦法如下:

    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(111001, TimeUnit.MINUTES, //
            new ArrayBlockingQueue<Runnable>(10000),//
            new DefaultThreadFactory()) {

        
    protected void afterExecute(Runnable r, Throwable t) {
            
    super.afterExecute(r, t);
            printException(r, t);
        }
    };

    private static void printException(Runnable r, Throwable t) {
        
    if (t == null && r instanceof Future<?>) {
            
    try {
                Future
    <?> future = (Future<?>) r;
                
    if (future.isDone())
                    future.get();
            } 
    catch (CancellationException ce) {
                t 
    = ce;
            } 
    catch (ExecutionException ee) {
                t 
    = ee.getCause();
            } 
    catch (InterruptedException ie) {
                Thread.currentThread().interrupt(); 
    // ignore/reset
            }
        }
        
    if (t != null)
            log.error(t.getMessage(), t);
    }

    此辦法的關(guān)鍵在于,事實(shí)上 afterExecute 并不會總是拋出異常 Throwable t,通過查看源碼得知,異常是封裝在此時(shí)的Future對象中的, 而此Future對象其實(shí)是一個(gè)java.util.concurrent.FutureTask的實(shí)現(xiàn),默認(rèn)的run方法其實(shí)調(diào)用的 java.util.concurrent.FutureTask.Sync.innerRun()。

    void innerRun() {
        if (!compareAndSetState(0, RUNNING))
            return;
        try {
            runner = Thread.currentThread();
            if (getState() == RUNNING) // recheck after setting thread
                innerSet(callable.call());
            else
                releaseShared(0); // cancel
        } catch (Throwable ex) {
            innerSetException(ex);
        }
    }

    void innerSetException(Throwable t) {
        for (;;) {
            int s = getState();
            if (s == RAN)
                return;
            if (s == CANCELLED) {
                // aggressively release to set runner to null,
                
    // in case we are racing with a cancel request
                
    // that will try to interrupt runner
                releaseShared(0);
                return;
            }
            if (compareAndSetState(s, RAN)) {
                exception = t;
                result = null;
                releaseShared(0);
                done();
                return;
            }
        }
    }

    這里我們可以看到它吃掉了異常,將異常存儲在java.util.concurrent.FutureTask.Sync的exception字段中:

    /** The exception to throw from get() */
    private Throwable exception;

    當(dāng)我們獲取異步執(zhí)行的結(jié)果時(shí), java.util.concurrent.FutureTask.get()

    public V get() throws InterruptedException, ExecutionException {
        
    return sync.innerGet();
    }

    java.util.concurrent.FutureTask.Sync.innerGet()

    V innerGet() throws InterruptedException, ExecutionException {
        acquireSharedInterruptibly(
    0);
        
    if (getState() == CANCELLED)
            
    throw new CancellationException();
        
    if (exception != null)
            
    throw new ExecutionException(exception);
        
    return result;
    }

    異常就會被包裝成ExecutionException異常拋出。

    也就是說當(dāng)我們想線程池 ThreadPoolExecutor(java.util.concurrent.ExecutorService)提交任務(wù)時(shí), 如果不理會任務(wù)結(jié)果(Feture.get()),那么此異常將被線程池吃掉。

    <T> Future<T> submit(Callable<T> task);
    Future
    <?> submit(Runnable task);

    而java.util.concurrent.ScheduledThreadPoolExecutor是繼承ThreadPoolExecutor的,因此情況類似。

    結(jié)論,通過覆蓋ThreadPoolExecutor.afterExecute 方法,我們才能捕獲到任務(wù)的異常(RuntimeException)。

    原文地址:http://imxylz.com/blog/2013/08/02/handling-the-uncaught-exception-of-java-thread-pool/



    ©2009-2014 IMXYLZ |求賢若渴
    posted on 2013-08-05 16:45 imxylz 閱讀(29864) 評論(6)  編輯  收藏 所屬分類: Java Concurrency

    評論

    # re: 捕獲Java線程池執(zhí)行任務(wù)拋出的異常 2013-08-08 10:34 陜西BOY
    V5的大濕重出江湖

    @Test
    public void testThread(){
    ThreadTest tt = new ThreadTest();
    tt.setUncaughtExceptionHandler(new LocalUncaughtExceptionHandler());
    tt.start();
    }  回復(fù)  更多評論
      

    # re: 捕獲Java線程池執(zhí)行任務(wù)拋出的異常[未登錄] 2013-08-15 10:24 Ryan
    呵呵~~哥們的博客,尤其是java并發(fā)這塊,很好很好哦。  回復(fù)  更多評論
      

    # re: 捕獲Java線程池執(zhí)行任務(wù)拋出的異常[未登錄] 2013-11-08 10:31 w
    dd  回復(fù)  更多評論
      

    # re: 捕獲Java線程池執(zhí)行任務(wù)拋出的異常 2015-05-12 22:59 liubey
    我想問下LZ,如果主線程想拿到子線程的異常,比如展示給界面,該怎么做=。=  回復(fù)  更多評論
      

    # re: 捕獲Java線程池執(zhí)行任務(wù)拋出的異常 2015-05-13 10:37 imxylz
    @liubey
    友好的做法是子線程不拋出異常,返回不同的結(jié)果,或者將異常封裝到return對象中。父對象根據(jù)此結(jié)果/異常封裝友好的提示給界面。  回復(fù)  更多評論
      

    # re: 捕獲Java線程池執(zhí)行任務(wù)拋出的異常 2015-05-26 13:55 liubey
    @imxylz
    謝謝指點(diǎn),你這種方式更優(yōu)雅些,我自己是new了個(gè)exceptionQueue,new線程的時(shí)候set進(jìn)去,然后執(zhí)行完子線程后查看這個(gè)Queue  回復(fù)  更多評論
      


    ©2009-2014 IMXYLZ
    主站蜘蛛池模板: 亚洲精品WWW久久久久久| 免费看男人j放进女人j免费看| 亚洲综合中文字幕无线码| 亚洲小说图片视频| 亚洲丰满熟女一区二区v| 亚洲精品视频观看| 亚洲精品国产啊女成拍色拍| 日产亚洲一区二区三区| 久久久亚洲欧洲日产国码二区| 久久精品国产精品亚洲色婷婷| 亚洲人成影院在线| 亚洲天堂福利视频| 亚洲欧洲日本在线观看| 亚洲精品蜜夜内射| 日韩免费在线中文字幕| 国产免费播放一区二区| 国产成人AV免费观看| 污视频在线免费观看| 无码国产精品一区二区免费式直播 | 国产四虎免费精品视频| 国产又大又粗又长免费视频| 成人黄18免费视频| 四虎影视免费永久在线观看| 狠狠色婷婷狠狠狠亚洲综合| 亚洲AV人无码综合在线观看 | 永久免费视频v片www| 免费人成在线观看网站视频| 亚洲综合色视频在线观看| 亚洲国产精品久久久天堂| 亚洲女人初试黑人巨高清| 亚洲GV天堂无码男同在线观看| 日韩毛片一区视频免费| 人人揉揉香蕉大免费不卡| 69天堂人成无码麻豆免费视频| 尤物永久免费AV无码网站| 亚洲中文字幕第一页在线| 亚洲精品一卡2卡3卡三卡四卡| 国产精品亚洲一区二区在线观看| 久久99精品免费一区二区| 久久免费看黄a级毛片 | 久久久久国产精品免费看|