在每個任務執行前後使用鉤子方法可以實現一些線程池輔助功能,例如線程池的暫停與恢復、打印日誌、統計等。
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("線程池恢復執行");
}
}