Java併發編程之線程池任務監控

Java併發編程之線程池任務監控

 

當我們提交runnable或者callable<?>到ThreadPoolExecutor時,我們是無法知道這些任務是在什麼時候才真正的執行的,爲了實現這個需求,我們需要擴展ThreadPoolExecutor,重寫beforeExecute和afterExecute,在這兩個方法裏分別做一些任務執行前和任務執行後的相關監控邏輯,還有個terminated方法,是在線程池關閉後回調,,另外,我們可以通過getLargestPoolSize()和getCompletedTaskCount()來分別獲取線程池數的峯值和線程池已完成的任務數。

 

下面就一個完整的例子來說明如何進行:

自定義MonitorHandler接口,把before和after抽象出來:

 

Java代碼 
  1. package cc.lixiaohui.demo.concurrent;  
  2.   
  3.   
  4. /** 
  5.  * 監控處理器, 目的是把before和after抽象出來, 以便在{@link MonitorableThreadPoolExecutor}中形成一條監控處理器鏈 
  6.  *  
  7.  * @author lixiaohui 
  8.  * @date 2016年10月11日 下午7:18:38 
  9.  *  
  10.  */  
  11. public interface MonitorHandler {  
  12.       
  13.     /** 
  14.      * 改監控任務是否可用 
  15.      *  
  16.      * @return 
  17.      */  
  18.     boolean usable();   
  19.       
  20.     /** 
  21.      * 任務執行前回調 
  22.      *  
  23.      * @param thread 即將執行該任務的線程 
  24.      * @param runnable 即將執行的任務 
  25.      */  
  26.     void before(Thread thread, Runnable runnable);    
  27.       
  28.     /** 
  29.      * <pre> 
  30.      * 任務執行後回調 
  31.      * 注意: 
  32.      *     1.當你往線程池提交的是{@link Runnable} 對象時, 參數runnable就是一個{@link Runnable}對象 
  33.      *     2.當你往線程池提交的是{@link java.util.concurrent.Callable<?>} 對象時, 參數runnable實際上就是一個{@link java.util.concurrent.FutureTask<?>}對象 
  34.      *       這時你可以通過把參數runnable downcast爲FutureTask<?>或者Future來獲取任務執行結果 
  35.      *        
  36.      * @param runnable 執行完後的任務 
  37.      * @param throwable 異常信息 
  38.      */  
  39.     void after(Runnable runnable, Throwable throwable);  
  40.       
  41.     /** 
  42.      * 線程池關閉後回調 
  43.      *  
  44.      * @param largestPoolSize 
  45.      * @param completedTaskCount 
  46.      */  
  47.     void terminated(int largestPoolSize, long completedTaskCount);  
  48. }  

 

 

擴展ThreadPoolExecutor,增加監控的邏輯,如果監控比較耗時的話,爲了不影響業務線程池的執行效率,我們應該將before,after和terminated方法的調用封裝爲統一的Runnable交給非業務線程池內的Thread來跑(新建個Thread或者線程池):

 

Java代碼 
  1. package cc.lixiaohui.demo.concurrent;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5. import java.util.concurrent.BlockingQueue;  
  6. import java.util.concurrent.RejectedExecutionHandler;  
  7. import java.util.concurrent.ThreadFactory;  
  8. import java.util.concurrent.ThreadPoolExecutor;  
  9. import java.util.concurrent.TimeUnit;  
  10.   
  11. /** 
  12.  * 可監控的線程池, 可有多個監控處理器,如果監控的邏輯是比較耗時的話, 最好另起個線程或者線程池專門用來跑MonitorHandler的方法. 
  13.  *  
  14.  * @author lixiaohui 
  15.  * @date 2016年10月11日 下午7:15:16 
  16.  *  
  17.  */  
  18. public class MonitorableThreadPoolExecutor extends ThreadPoolExecutor {  
  19.       
  20.     /** 
  21.      * 可有多個監控處理器 
  22.      */  
  23.     private Map<String, MonitorHandler> handlerMap = new HashMap<String, MonitorHandler>();  
  24.       
  25.     private final Object lock = new Object();  
  26.       
  27.     public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {  
  28.         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);  
  29.     }  
  30.   
  31.     public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {  
  32.         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);  
  33.     }  
  34.   
  35.     public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {  
  36.         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);  
  37.     }  
  38.   
  39.     public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {  
  40.         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);  
  41.     }  
  42.       
  43.     @Override  
  44.     protected void beforeExecute(Thread t, Runnable r) {  
  45.         super.beforeExecute(t, r);  
  46.         // 依次調用處理器  
  47.         for (MonitorHandler handler : handlerMap.values()) {  
  48.             if (handler.usable()) {  
  49.                 handler.before(t, r);  
  50.             }  
  51.         }  
  52.     }  
  53.       
  54.     @Override  
  55.     protected void afterExecute(Runnable r, Throwable t) {  
  56.         super.afterExecute(r, t);  
  57.         // 依次調用處理器  
  58.         for (MonitorHandler handler : handlerMap.values()) {  
  59.             if (handler.usable()) {  
  60.                 handler.after(r, t);  
  61.             }  
  62.         }  
  63.     }  
  64.       
  65.     /*  
  66.      * @see java.util.concurrent.ThreadPoolExecutor#terminated() 
  67.      */  
  68.     @Override  
  69.     protected void terminated() {  
  70.         super.terminated();  
  71.         for (MonitorHandler handler : handlerMap.values()) {  
  72.             if (handler.usable()) {  
  73.                 handler.terminated(getLargestPoolSize(), getCompletedTaskCount());  
  74.             }  
  75.         }  
  76.           
  77.     }  
  78.       
  79.     public MonitorHandler addMonitorTask(String key, MonitorHandler task, boolean overrideIfExist) {  
  80.         if (overrideIfExist) {  
  81.             synchronized (lock) {  
  82.                 return handlerMap.put(key, task);  
  83.             }  
  84.         } else {  
  85.             synchronized (lock) {  
  86.                 return handlerMap.putIfAbsent(key, task);  
  87.             }  
  88.         }  
  89.     }  
  90.       
  91.     public MonitorHandler addMonitorTask(String key, MonitorHandler task) {  
  92.         return addMonitorTask(key, task, true);  
  93.     }  
  94.       
  95.     public MonitorHandler removeMonitorTask(String key) {  
  96.         synchronized (lock) {  
  97.             return handlerMap.remove(key);  
  98.         }  
  99.     }  
  100.       
  101. }  

 

 

 測試程序:

 

Java代碼 
  1. package cc.lixiaohui.demo.concurrent;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Map;  
  5. import java.util.concurrent.Callable;  
  6. import java.util.concurrent.CancellationException;  
  7. import java.util.concurrent.ConcurrentHashMap;  
  8. import java.util.concurrent.ExecutionException;  
  9. import java.util.concurrent.Future;  
  10. import java.util.concurrent.FutureTask;  
  11. import java.util.concurrent.LinkedBlockingQueue;  
  12. import java.util.concurrent.TimeUnit;  
  13.   
  14. import cc.lixiaohui.util.RandomUtils;  
  15.   
  16. /** 
  17.  * @author lixiaohui 
  18.  * @date 2016年10月11日 下午8:11:39 
  19.  *  
  20.  */  
  21. public class Tester {  
  22.       
  23.     static volatile boolean stop = false;  
  24.   
  25.     public static void main(String[] args) throws InterruptedException, IOException {  
  26.         // fixed size 5  
  27.         final MonitorableThreadPoolExecutor pool = new MonitorableThreadPoolExecutor(51030, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());  
  28.   
  29.         pool.addMonitorTask("TimeMonitorTask", newTimeMonitorHandler());  
  30.         // 起一個線程不斷地往線程池丟任務  
  31.         Thread t = new Thread(new Runnable() {  
  32.             public void run() {  
  33.                 startAddTask(pool);  
  34.             }  
  35.         });  
  36.         t.start();  
  37.           
  38.         // 丟任務丟20 ms  
  39.         Thread.sleep(50);  
  40.         stop = true;  
  41.         t.join();  
  42.         pool.shutdown();  
  43.         // 等線程池任務跑完  
  44.         pool.awaitTermination(100, TimeUnit.SECONDS);  
  45.     }  
  46.   
  47.     private static MonitorHandler newTimeMonitorHandler() {  
  48.   
  49.         return new MonitorHandler() {  
  50.             // 任務開始時間記錄map, 多線程增刪, 需用ConcurrentHashMap  
  51.             Map<Runnable, Long> timeRecords = new ConcurrentHashMap<Runnable, Long>();  
  52.   
  53.             public boolean usable() {  
  54.                 return true;  
  55.             }  
  56.               
  57.             public void terminated(int largestPoolSize, long completedTaskCount) {  
  58.                 System.out.println(String.format("%s:largestPoolSize=%d, completedTaskCount=%s", time(), largestPoolSize, completedTaskCount));  
  59.             }  
  60.   
  61.             public void before(Thread thread, Runnable runnable) {  
  62.                 System.out.println(String.format("%s: before[%s -> %s]", time(), thread, runnable));  
  63.                 timeRecords.put(runnable, System.currentTimeMillis());  
  64.             }  
  65.   
  66.             public void after(Runnable runnable, Throwable throwable) {  
  67.                 long end = System.currentTimeMillis();  
  68.                 Long start = timeRecords.remove(runnable);  
  69.                   
  70.                 Object result = null;  
  71.                 if (throwable == null && runnable instanceof FutureTask<?>) { // 有返回值的異步任務,不一定是Callable<?>,也有可能是Runnable  
  72.                     try {  
  73.                         result = ((Future<?>) runnable).get();  
  74.                     } catch (InterruptedException e) {  
  75.                         Thread.currentThread().interrupt(); // reset  
  76.                     } catch (ExecutionException e) {  
  77.                         throwable = e;  
  78.                     } catch (CancellationException e) {  
  79.                         throwable = e;  
  80.                     }  
  81.                 }  
  82.   
  83.                 if (throwable == null) { // 任務正常結束  
  84.                     if (result != null) { // 有返回值的異步任務  
  85.                         System.out.println(String.format("%s: after[%s -> %s], costs %d millisecond, result: %s", time(), Thread.currentThread(), runnable, end - start, result));  
  86.                     } else {  
  87.                         System.out.println(String.format("%s: after[%s -> %s], costs %d millisecond", time(), Thread.currentThread(), runnable, end - start));  
  88.                     }  
  89.                 } else {  
  90.                     System.err.println(String.format("%s: after[%s -> %s], costs %d millisecond, exception: %s", time(), Thread.currentThread(), runnable, end - start, throwable));  
  91.                 }  
  92.             }  
  93.   
  94.         };  
  95.     }  
  96.   
  97.     // 隨機runnable或者callable<?>, 任務隨機拋異常  
  98.     private static void startAddTask(MonitorableThreadPoolExecutor pool) {  
  99.         int count = 0;  
  100.         while (!stop) {  
  101.             if (RandomUtils.randomBoolean()) {// 丟Callable<?>任務  
  102.                 pool.submit(new Callable<Boolean>() {  
  103.   
  104.                     public Boolean call() throws Exception {  
  105.                         // 隨機拋異常  
  106.                         boolean bool = RandomUtils.randomBoolean();  
  107.                         // 隨機耗時 0~100 ms  
  108.                         Thread.sleep(RandomUtils.randomInt(100));  
  109.                         if (bool) {  
  110.                             throw new RuntimeException("thrown randomly");  
  111.                         }  
  112.                         return bool;  
  113.                     }  
  114.   
  115.                 });  
  116.             } else { // 丟Runnable  
  117.                 pool.submit(new Runnable() {  
  118.   
  119.                     public void run() {  
  120.                         // 隨機耗時 0~100 ms  
  121.                         try {  
  122.                             Thread.sleep(RandomUtils.randomInt(100));  
  123.                         } catch (InterruptedException e) {}  
  124.                         // 隨機拋異常  
  125.                         if (RandomUtils.randomBoolean()) {  
  126.                             throw new RuntimeException("thrown randomly");  
  127.                         }  
  128.                     };  
  129.   
  130.                 });  
  131.             }  
  132.             System.out.println(String.format("%s:submitted %d task", time(), ++count));  
  133.         }  
  134.     }  
  135.   
  136.     private static String time() {  
  137.         return String.valueOf(System.currentTimeMillis());  
  138.     }  
  139. }  

 

 

一個較短的結果:

 

Java代碼 
  1. 1476253228222: before[Thread[pool-1-thread-1,5,main] -> java.util.concurrent.FutureTask@548bb979]  
  2. 1476253228222:Thread[Thread-0,5,main], submitted 1 task  
  3. 1476253228253:Thread[Thread-0,5,main], submitted 2 task  
  4. 1476253228264: before[Thread[pool-1-thread-2,5,main] -> java.util.concurrent.FutureTask@97e041d]  
  5. 1476253228264:Thread[Thread-0,5,main], submitted 3 task  
  6. 1476253228265: before[Thread[pool-1-thread-3,5,main] -> java.util.concurrent.FutureTask@7d6d5cc]  
  7. 1476253228271: after[Thread[pool-1-thread-2,5,main] -> java.util.concurrent.FutureTask@97e041d], costs 7 millisecond, exception: java.util.concurrent.ExecutionException: java.lang.RuntimeException: thrown randomly  
  8. 1476253228295: after[Thread[pool-1-thread-1,5,main] -> java.util.concurrent.FutureTask@548bb979], costs 42 millisecond  
  9. 1476253228347: after[Thread[pool-1-thread-3,5,main] -> java.util.concurrent.FutureTask@7d6d5cc], costs 82 millisecond, exception: java.util.concurrent.ExecutionException: java.lang.RuntimeException: thrown randomly  
  10. 1476253228347:Thread[pool-1-thread-3,5,main], largestPoolSize=3, completedTaskCount=3  
發佈了50 篇原創文章 · 獲贊 16 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章