Thread pool task monitoring for concurrent programming in Java

Thread pool task monitoring for concurrent programming in Java

 

When we submit runnable or callable<?> to ThreadPoolExecutor, we cannot know when these tasks are actually executed. In order to achieve this requirement, we need to extend ThreadPoolExecutor, rewrite beforeExecute and afterExecute, in these two methods There are some related monitoring logic before and after task execution. There is also a terminated method, which is called back after the thread pool is closed. In addition, we can get the number of thread pools through getLargestPoolSize() and getCompletedTaskCount() respectively. Peak and thread pool completed tasks.

 

Here's a complete example to illustrate how:

Customize the MonitorHandler interface to abstract before and after:

 

package cc.lixiaohui.demo.concurrent;


/**
 * Monitor processor, the purpose is to abstract before and after to form a monitor processor chain in {@link MonitorableThreadPoolExecutor}
 *
 * @author lixiaohui
 *@date October 11, 2016 at 7:18:38 PM
 *
 */
public interface MonitorHandler {
	
	/**
	 * Change whether the monitoring task is available
	 *
	 * @return
	 */
	boolean usable();
	
	/**
	 * Callback before task execution
	 *
	 * @param thread the thread that will execute the task
	 * @param runnable the task to be executed
	 */
	void before(Thread thread, Runnable runnable);  
	
	/**
	 * <pre>
	 * Callback after task execution
	 * Notice:
	 * 1. When you submit a {@link Runnable} object to the thread pool, the parameter runnable is a {@link Runnable} object
	 * 2. When you submit a {@link java.util.concurrent.Callable<?>} object to the thread pool, the parameter runnable is actually a {@link java.util.concurrent.FutureTask<?>} object
	 * At this time, you can get the task execution result by setting the parameter runnable downcast to FutureTask<?> or Future
	 *       
	 * @param runnable task after execution
	 * @param throwable exception information
	 */
	void after(Runnable runnable, Throwable throwable);
	
	/**
	 * Callback after the thread pool is closed
	 *
	 * @param largestPoolSize
	 * @param completedTaskCount
	 */
	void terminated(int largestPoolSize, long completedTaskCount);
}

 

 

Extend ThreadPoolExecutor and add monitoring logic. If monitoring is time-consuming, in order not to affect the execution efficiency of the business thread pool, we should encapsulate the calls of the before, after and terminated methods as a unified Runnable and hand it over to the Thread in the non-business thread pool. Run (create a new Thread or thread pool):

 

package cc.lixiaohui.demo.concurrent;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * The monitorable thread pool can have multiple monitoring processors. If the monitoring logic is time-consuming, it is best to create a separate thread or thread pool to run the MonitorHandler method.
 *
 * @author lixiaohui
 *@date October 11, 2016 at 7:15:16 PM
 *
 */
public class MonitorableThreadPoolExecutor extends ThreadPoolExecutor {
	
	/**
	 * Can have multiple monitoring processors
	 */
	private Map<String, MonitorHandler> handlerMap = new HashMap<String, MonitorHandler>();
	
	private final Object lock = new Object();
	
	public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
	}

	public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
	}

	public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
	}

	public MonitorableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
	}
	
	@Override
	protected void beforeExecute(Thread t, Runnable r) {
		super.beforeExecute(t, r);
		// call the handler in turn
		for (MonitorHandler handler : handlerMap.values()) {
			if (handler.usable()) {
				handler.before(t, r);
			}
		}
	}
	
	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		super.afterExecute(r, t);
		// call the handler in turn
		for (MonitorHandler handler : handlerMap.values()) {
			if (handler.usable()) {
				handler.after(r, t);
			}
		}
	}
	
	/*
	 * @see java.util.concurrent.ThreadPoolExecutor#terminated()
	 */
	@Override
	protected void terminated() {
		super.terminated();
		for (MonitorHandler handler : handlerMap.values()) {
			if (handler.usable()) {
				handler.terminated(getLargestPoolSize(), getCompletedTaskCount());
			}
		}
		
	}
	
	public MonitorHandler addMonitorTask(String key, MonitorHandler task, boolean overrideIfExist) {
		if (overrideIfExist) {
			synchronized (lock) {
				return handlerMap.put(key, task);
			}
		} else {
			synchronized (lock) {
				return handlerMap.putIfAbsent(key, task);
			}
		}
	}
	
	public MonitorHandler addMonitorTask(String key, MonitorHandler task) {
		return addMonitorTask(key, task, true);
	}
	
	public MonitorHandler removeMonitorTask(String key) {
		synchronized (lock) {
			return handlerMap.remove(key);
		}
	}
	
}

 

 

 test program:

 

package cc.lixiaohui.demo.concurrent;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import cc.lixiaohui.util.RandomUtils;

/**
 * @author lixiaohui
 *@date October 11, 2016 at 8:11:39 PM
 *
 */
public class Tester {
	
	static volatile boolean stop = false;

	public static void main(String[] args) throws InterruptedException, IOException {
		// fixed size 5
		final MonitorableThreadPoolExecutor pool = new MonitorableThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

		pool.addMonitorTask("TimeMonitorTask", newTimeMonitorHandler());
		// Start a thread and keep throwing tasks to the thread pool
		Thread t = new Thread(new Runnable() {
			public void run() {
				startAddTask (pool);
			}
		});
		t.start();
		
		// Lose the task and lose 20 ms
		Thread.sleep(50);
		stop = true;
		t.join();
		pool.shutdown();
		// Wait for the thread pool task to finish running
		pool.awaitTermination(100, TimeUnit.SECONDS);
	}

	private static MonitorHandler newTimeMonitorHandler() {

		return new MonitorHandler() {
			// The task start time record map, multi-threaded additions and deletions, need to use ConcurrentHashMap
			Map<Runnable, Long> timeRecords = new ConcurrentHashMap<Runnable, Long>();

			public boolean usable() {
				return true;
			}
			
			public void terminated(int largestPoolSize, long completedTaskCount) {
				System.out.println(String.format("%s:largestPoolSize=%d, completedTaskCount=%s", time(), largestPoolSize, completedTaskCount));
			}

			public void before(Thread thread, Runnable runnable) {
				System.out.println(String.format("%s: before[%s -> %s]", time(), thread, runnable));
				timeRecords.put(runnable, System.currentTimeMillis());
			}

			public void after(Runnable runnable, Throwable throwable) {
				long end = System.currentTimeMillis();
				Long start = timeRecords.remove(runnable);
				
				Object result = null;
				if (throwable == null && runnable instanceof FutureTask<?>) { // An asynchronous task with a return value, not necessarily Callable<?>, but also Runnable
					try {
						result = ((Future<?>) runnable).get();
					} catch (InterruptedException e) {
						Thread.currentThread().interrupt(); // reset
					} catch (ExecutionException e) {
						throwable = e;
					} catch (CancellationException e) {
						throwable = e;
					}
				}

				if (throwable == null) { // task ends normally
					if (result != null) { // async task with return value
						System.out.println(String.format("%s: after[%s -> %s], costs %d millisecond, result: %s", time(), Thread.currentThread(), runnable, end - start, result));
					} else {
						System.out.println(String.format("%s: after[%s -> %s], costs %d millisecond", time(), Thread.currentThread(), runnable, end - start));
					}
				} else {
					System.err.println(String.format("%s: after[%s -> %s], costs %d millisecond, exception: %s", time(), Thread.currentThread(), runnable, end - start, throwable));
				}
			}

		};
	}

	// Random runnable or callable<?>, the task throws an exception randomly
	private static void startAddTask(MonitorableThreadPoolExecutor pool) {
		int count = 0;
		while (!stop) {
			if (RandomUtils.randomBoolean()) {// Lose the Callable<?> task
				pool.submit(new Callable<Boolean>() {

					public Boolean call() throws Exception {
						// throw exception randomly
						boolean bool = RandomUtils.randomBoolean();
						// Random time 0~100 ms
						Thread.sleep(RandomUtils.randomInt(100));
						if (bool) {
							throw new RuntimeException("thrown randomly");
						}
						return bool;
					}

				});
			} else { // 丢Runnable
				pool.submit(new Runnable() {

					public void run() {
						// Random time 0~100 ms
						try {
							Thread.sleep(RandomUtils.randomInt(100));
						} catch (InterruptedException e) {}
						// throw exception randomly
						if (RandomUtils.randomBoolean()) {
							throw new RuntimeException("thrown randomly");
						}
					};

				});
			}
			System.out.println(String.format("%s:submitted %d task", time(), ++count));
		}
	}

	private static String time() {
		return String.valueOf(System.currentTimeMillis());
	}
}

 

 

A shorter result:

 

1476253228222: before[Thread[pool-1-thread-1,5,main] -> java.util.concurrent.FutureTask@548bb979]
1476253228222:Thread[Thread-0,5,main], submitted 1 task
1476253228253:Thread[Thread-0,5,main], submitted 2 task
1476253228264: before[Thread[pool-1-thread-2,5,main] -> java.util.concurrent.FutureTask@97e041d]
1476253228264:Thread[Thread-0,5,main], submitted 3 task
1476253228265: before[Thread[pool-1-thread-3,5,main] -> java.util.concurrent.FutureTask@7d6d5cc]
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
1476253228295: after[Thread[pool-1-thread-1,5,main] -> java.util.concurrent.FutureTask@548bb979], costs 42 millisecond
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
1476253228347:Thread[pool-1-thread-3,5,main], largestPoolSize=3, completedTaskCount=3

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326186357&siteId=291194637