Java线程池该如何监控?
日常开发中,当我们开发一些复合型任务时,尝尝会使用线程池通过异步的方式解决一些对时效性要求不高的任务。下面小编列举几种线程池的使用方式,以便参考!
Java JDK中默认封装好的Executors类:
下面简单的列举三种我们常用的线程池操作类:
public static void main(String[] args) {
//创建大小为4个线程的线程池
ExecutorService executorService1 = Executors.newFixedThreadPool(4);
//创建一个单独线程的线程池
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
//创建缓存行线程池
ExecutorService executorService3 = Executors.newCachedThreadPool();
}
这段代码的优点也是显而易见的,就是操作线程池的便利性,我们可以非常方便的使用线程池来结合到我们的业务开发中。
但往往事物都两面性,这段代码的缺点就是可能导致OOM,因为其内部是一个无解队列,当你的任务数远远大于你的线程池数量时,缓存队列则会一直被追加,直到把你当前机器的内存塞满,最终导致OOM事件。
ThreadPoolExecutor类
根据Executors类的源码得知,内部其实是通过new ThreadPoolExecutor类进行实现的,下面我们来看下Executors.newFixedThreadPool的源码实现:
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
其他几种的Executors方法实现方式都大同小异,不一一列举。
回到主题,在日常中开发如何监控线程池呢,这时就需要我们刚才所说的ThreadPoolExecutor类。
通过ThreadPoolExecutor实现监控
其实监控线程池很简单,我们只要继承ThreadPoolExecutor就可以得到所有我们想要的,下面代码是继承后,所必须要重写几个构造函数重载。
public class MyThreadPool extends ThreadPoolExecutor {
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
}
其实小编个人认为,最方便的还是通过重写execute、shutdown最为实用,可以达到我们监控或统一处理某些业务场景的实现,下面列举一些示例仅供参考:
/**
* 执行线程任务前执行
*
* @param t
* @param r
*/
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
}
/**
* 执行线程时调用
*
* @param command
*/
@Override
public void execute(Runnable command) {
//当前核心线程大小
this.getCorePoolSize();
//最大线程数大小
this.getMaximumPoolSize();
//当前线程池任务数量
this.getTaskCount();
//当前队列
this.getQueue();
//设置核心线程数量
this.setCorePoolSize(4);
//设置最大线程数
this.setMaximumPoolSize(4);
super.execute(command);
}
/**
* 执行线程任务后执行
*
* @param r
* @param t
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
}
/**
* 结束线程池时执行
*/
@Override
public void shutdown() {
super.shutdown();
}
通过上述代码可以得知,当继承ThreadPoolExecutor之后,我们可以方便的拿到当前线程池的coreSIze、maxiMumSize等等。这样我们不仅能够实时的去监控线程池的状态,同样可以通过setCorePoolSize等方法实现动态扩容,达到我们监控的目的。
其实我们也可以实时监控内存队列的大小,当达到某个预警值的时候进行报警,都可以很方便的实现。
总结:
小编本次不做深入的讲解,你希望帮助大家简单的认识下ThreadPoolExecutor的扩展方式,有什么问题也希望大家及时提问,欢迎可以共同探讨的同学,谢谢!