在每个任务执行前后使用钩子方法可以实现一些线程池辅助功能,例如线程池的暂停与恢复、打印日志、统计等。
ThreadPoolExecutor提供了3个钩子方法,需要子类根据需要重写方法。对于三个钩子方法的使用,参见runWoker方法和tryTerminate方法。
protected void beforeExecute(Thread t, Runnable r) { } // 任务执行前
protected void afterExecute(Runnable r, Throwable t) { } // 任务执行后
protected void terminated() { } // 线程池执行结束后
示例:使用beforeExecute()辅助实现线程池的暂停与恢复
public class PauseableThreadPool extends ThreadPoolExecutor {
/**
* 显式锁
*/
private final ReentrantLock lock = new ReentrantLock();
private Condition unpaused = lock.newCondition();
private boolean isPaused;
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
/**
* 每个任务执行前执行,识别标记位
*
* @param t
* @param r
*/
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
lock.lock();
try {
while (isPaused) {
unpaused.await(); // 阻塞挂起,释放锁
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// 暂停与恢复
private void pause() {
lock.lock();
try {
isPaused = true;
} finally {
lock.unlock();
}
}
private void resume() {
lock.lock();
try {
isPaused = false;
unpaused.signalAll(); // 通知唤醒
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
PauseableThreadPool pool = new PauseableThreadPool(10, 20, 100, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
Runnable task = () -> {
System.out.println("任务被执行" + Thread.currentThread().getName());
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
for (int i = 0; i < 10000; i++) {
pool.execute(task);
}
TimeUnit.SECONDS.sleep(1);
pool.pause();
System.out.println("线程池暂停");
TimeUnit.SECONDS.sleep(10);
pool.resume();
System.out.println("线程池恢复执行");
}
}