2016-02-12 58 views
5

優點:重寫ThreadPoolExecutor afterExecute方法 - 任何缺點?的鉤方法

beforeExecute(Thread, Runnable)afterExecute(Runnable, Throwable)

beforeExecute(Thread, Runnable)和afterExecute(Runnable, Throwable)方法之前和每個任務的執行之後被調用。這些可以用來操縱執行環境;例如,重新初始化ThreadLocals,收集統計信息或添加日誌條目

我正在使用自定義ThreadPoolExecutor來處理未捕獲的異常。我可以在RunnableCallable中添加try{} catch{}塊,但假設您不能強制開發人員在相關的Runnable和Callable任務中添加這些塊。

CustomThreadPoolExecutor,覆蓋afterExecute()方法在ThreadPoolExecutor如下(I已分配變量b的值爲零以模擬算術異常。

import java.util.concurrent.*; 
import java.util.*; 

class CustomThreadPoolExecutor extends ThreadPoolExecutor { 

    public CustomThreadPoolExecutor() { 
     super(1,10,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1000)); 
    } 

    protected void afterExecute(Runnable r, Throwable t) { 
    super.afterExecute(r, t); 
    if (t == null && r instanceof Future<?>) { 
     try { 
     Object result = ((Future<?>) r).get(); 
     System.out.println(result); 
     } catch (CancellationException ce) { 
      t = ce; 
     } catch (ExecutionException ee) { 
      t = ee.getCause(); 
     } catch (InterruptedException ie) { 
      Thread.currentThread().interrupt(); // ignore/reset 
     } 
    } 
    if (t != null) 
     t.printStackTrace(); 
    } 
} 


public class CustomThreadPoolExecutorDemo{ 

    public static void main(String args[]){ 
     System.out.println("creating service"); 
     //ExecutorService service = Executors.newFixedThreadPool(10); 
     CustomThreadPoolExecutor service = new CustomThreadPoolExecutor(); 
     service.submit(new Runnable(){ 
       public void run(){ 
        int a=4, b = 0; 
        System.out.println("a and b="+a+":"+b); 
        System.out.println("a/b:"+(a/b)); 
        System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName()); 
       } 
      }); 
     service.shutdown(); 
    } 
} 

由於submit()在框架隱藏例外,我已重寫afterExecute()方法捕獲異常。

在這種方法中,我添加了阻塞調用下面的語句

Object result = ((Future<?>) r).get(); 

目前我有10個線程,隊列容量爲1000.假設我的Runnable需要5秒鐘才能完成。

通過覆蓋afterExecute()方法,我是否會招致任何性能開銷或任何與此方法的利弊?

+2

看猜測性能不是一件好事,只是基準有和沒有覆蓋的代碼,並檢查是否有相關的變化。 http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Jack

回答

1

不,你的阻塞調用不會帶來額外的開銷,因爲任務已經完成執行並具有status >= NORMAL,你可以在void runWorker(Worker w)

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); 
} 
+0

但有些時候,我的get()在情境中阻塞,當我有服務間通信問題時(service.submit()和call future.get()) –

+0

是的,'get()'可以阻塞,但方法'afterExecute Runnable r,Throwable t)'是受保護的,並且(如果您沒有更改'FutureTask'語義)在任務完成後在'ThreadPoolExecutor'中_only_被調用。 – dezhik

1

比較好的解決辦法,抓住未來從submit()回來,然後你可以處理你的主線程的異常,而不是黑客執行人將其打印出來給你。

另一種方法是使用可運行一個共同的基礎,它實現你的願望異常處理,如:

public abstract class BaseRunnable implements Runnable { 
    public final run() { 
    try { 
     runImpl(); 
    } catch(Throwable t) { 
     t.printStackTrace(); 
    } 
    } 
    protected abstract runImpl() throws Exception; 
} 
+0

這不會與'FutureTask's捕捉異常和'setException(前);'並且不能與'CancellationException'一起使用。例外將永遠被捕獲。 – dezhik

+0

但在這種情況下,future.get()會阻塞主線程。 –

+0

@dezthink:如果你在主線程中添加了下面的代碼(我之前已經完成了它(在overidding afterExecute方法之前):未來),即使Callable也可以工作。{} catch {} block –