前两篇中我们介绍了什么是线程池 ,线程池的主要参数,快速手写一个简单的线程池,这一篇中我们介绍一些,线程池的重要拓展。
自定义线程创建
在之前例子里面 我创建线程池 参数只有四个,还有两个是线程工厂和拒绝策略。
这里的线程工厂作为参数的意义就是可以让我们自定义线程的创建:
比如 自定义线程的名称。 自定义线程的优先级。 改变线程是否为守护线程等等
我们就在上一篇的基础上改
public CustomThreadPool(){
executorService = new ThreadPoolExecutor(
5,
6,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread();
thread.setName("定制化名称");
thread.setDaemon(true);
thread.setPriority(3);//优先级
return thread;
}
});
}
拓展线程池
当我们想要更详细的监控每个线程执行的时间 开始和结束
ThreadPoolExecutor 提供了三个方法:可以做到有头有尾
public class MyThreadPoolExecutor extends ThreadPoolExecutor {
// 继承 ThreadPoolExecutor 类并重写 beforeExecute 方法
protected void beforeExecute(Thread t, Runnable r) {
// 在任务执行之前,记录任务信息和线程信息
System.out.println("Task is about to execute: " + r.toString());
System.out.println("Thread name: " + t.getName());
}
// 构造方法和其他自定义逻辑...
}
// 在使用时创建自定义的线程池
ExecutorService executor = new MyThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
// 提交任务给线程池
executor.submit(new MyTask());
正确的处理任务异常
在每一个线程池的run()执行方法中,万一有个什么三长两短,跑挂了。它是不会抛出异常的,程序也不会停止。 这对我们排查信息很重要。
这里推荐两个方法:
1.在run内手动捕获 并打印堆栈。
public class PrintTimeTask implements Runnable{
@Override
public void run() {
try {
System.out.println("当前线程:"+Thread.currentThread().getName()+"-打印时间:"+System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
//log.error()
}
}
}
2.使用future对象。使用submit提交task
它会返回一个future的结果对象。 这里面会记录线程的运行情况 及错误日志
// customThreadPool.execute(timeTask);
Future future = customThreadPool.submit(timeTask);
future.get();
这三种线程池的拓展希望新手自己多敲几遍熟练掌握