1、ThreadPoolExecutor
我们知道ThreadPoolExecutor是可扩展的,它提供了几个可以在子类中改写的空方法如下:
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
2、为什么要进行扩展?
因为在实际应用中,可以对线程池运行状态进行跟踪,输出一些有用的调试信息,以帮助故障诊断。
3、ThreadPoolExecutor.Worker的run方法实现
通过看源码我们发现 ThreadPoolExecutor的工作线程其实就是Worker实例,Worker.runTask()会被线程池以多线程模式异步调用,
则以上三个方法也将被多线程同时访问。
// 基于jdk1.8.0_161
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
4、扩展线程池实现:
public class ExtThreadPool {
public static class MyTask implements Runnable {
public String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("正在执行:Thread ID:" + Thread.currentThread().getId() + ",Task Name:" + name);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String args[]) throws InterruptedException {
ExecutorService executorService = new ThreadPoolExecutor(
5,
5,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<Runnable>()) {
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("准备执行:" + ((MyTask) r).name);
}
protected void afterExecute(Thread t, Runnable r) {
System.out.println("执行完成" + ((MyTask) r).name);
}
protected void terminated() {
System.out.println("线程池退出!");
}
};
for (int i = 0; i < 5; i++) {
MyTask task = new MyTask("TASK--" + i);
executorService.execute(task);
Thread.sleep(10);
}
executorService.shutdown();
}
}
执行结果如下:
准备执行:TASK–0
正在执行:Thread ID:10,Task Name:TASK–0
准备执行:TASK–1
正在执行:Thread ID:11,Task Name:TASK–1
准备执行:TASK–2
正在执行:Thread ID:12,Task Name:TASK–2
准备执行:TASK–3
正在执行:Thread ID:13,Task Name:TASK–3
准备执行:TASK–4
正在执行:Thread ID:14,Task Name:TASK–4
线程池退出!
这样就实现了在先吃执行前后进行的一些控制,除此之外我们还可以进行输出每个线程的执行时间,或者一些其他增强操作。
下次我们聊聊Java线程池的拒绝策略,吃瓜群众欢迎━(`∀´)ノ亻!围观~~
5、思考?
- 请读者思考shutdownNow和shutdown方法的区别?
- 在生产上关闭线程池的最佳实践是什么?