Java并发编程之线程池任务监控

时间:2021-04-11 18:02:48

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