一、什么是线程池?
当需要处理大量任务时,每次都新建线程会消耗大量的系统资源,降低程序效率。线程池则可以解决这个问题,它是一种先创建好若干个线程,放到一个“池”中,按需分配使用的技术。
二、为何需要监控线程池?
尽管线程池在处理任务上有优势,但是如果没有监控机制,在线程池中可能会遇到以下问题:
1、大量任务积压导致线程池中的线程耗尽,没有足够的线程处理新的任务,导致任务等待时间过长;
2、线程池中有无效线程或线程阻塞,占用系统资源,但是并没有发挥作用;
3、线程池中的线程没有明确地完成任务,没有及时释放资源。
三、如何监控线程池?
1、使用ThreadPoolExecutor类创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, //线程池中核心线程数 maxPoolSize, //线程池中最大线程数 keepAliveTime, //线程池中线程空闲保持时间 TimeUnit.MILLISECONDS, //线程池中线程空闲保持时间单位 workQueue, //阻塞队列,暂存等待执行的任务 threadFactory,//线程池中线程创建工厂 rejectHandler); //拒绝执行任务的处理器
2、添加线程池监控
可以通过扩展ThreadPoolExecutor类的beforeExecute、afterExecute和terminated方法,对线程池进行监控。
(1)beforeExecute
在每个执行任务前会被调用,可以记录开始执行任务的时间,以及任务信息等。
protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); taskStartTime.set(System.currentTimeMillis()); //记录任务信息等 }
(2)afterExecute
在每个执行任务后会被调用,可以记录任务执行结束时间,以及任务执行结果等。
protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); long duration = System.currentTimeMillis() - taskStartTime.get(); //记录任务执行时间和结果等 }
(3)terminated
在线程池关闭后会被调用,可以记录线程池关闭的时间。
protected void terminated() { super.terminated(); //记录线程池关闭时间等 }
四、如何优化线程池?
1、线程池大小的优化
线程池大小要根据实际场景来定,过大会浪费资源,过小会导致任务等待时间过长。可以根据工作负载进行动态调整,提高性能。
2、合理配置阻塞队列大小
阻塞队列越大,线程等待的时间就越长,因此需要根据实际情况来进行调整。
3、合理配置保持时间
保持时间过短会导致频繁地创建和销毁线程,因此需要根据线程池的实际情况来进行调整。
五、代码示例
1、线程池监控示例
public class MyThreadPoolExecutor extends ThreadPoolExecutor { private ThreadLocaltaskStartTime = new ThreadLocal<>(); public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); taskStartTime.set(System.currentTimeMillis()); //记录任务信息等 } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); long duration = System.currentTimeMillis() - taskStartTime.get(); //记录任务执行时间和结果等 } @Override protected void terminated() { super.terminated(); //记录线程池关闭时间等 } }
2、创建线程池示例
MyThreadPoolExecutor executor = new MyThreadPoolExecutor( corePoolSize, //线程池中核心线程数 maxPoolSize, //线程池中最大线程数 keepAliveTime, //线程池中线程空闲保持时间 TimeUnit.MILLISECONDS, //线程池中线程空闲保持时间单位 workQueue, //阻塞队列,暂存等待执行的任务 threadFactory,//线程池中线程创建工厂 rejectHandler); //拒绝执行任务的处理器