通过一次代码校验发现 线程池不建议使用Executors去创建,而是通过ThreadPoolExecutor方式的原因 顺便总结线程优缺点
阅读原文时间:2021年04月20日阅读:1

今天在用P3C检查代码的时候发现这样一个警告:

首先用的是new Thread

public void doSummaryJob() throws Exception {
        try{
            HandlerMappingSummaryJobRunnable handlerMappingSummaryJobRunnable = new HandlerMappingSummaryJobRunnable();
            Thread summaryJobThread = new Thread(handlerMappingSummaryJobRunnable);
            summaryJobThread.start();
        }catch(Exception e){
            throw new Exception(HandlerMappingStaticValue.LOG_DO_SUMMARY_JOB_ERROR+e.getMessage());
        }
    }

然后爆出下面的建议


于是我改成Executors工厂创建线程池

HandlerMappingSummaryJobRunnable handlerMappingSummaryJobRunnable = new HandlerMappingSummaryJobRunnable();
             // 定义一个线程池
            ExecutorService executor = Executors.newCachedThreadPool();
                executor.execute(() -> {
                    try {
                     handlerMappingSummaryJobRunnable.run();//执行 方法
                    } catch (Exception e) {
                     e.printStackTrace();
                    }finally{
                     executor.shutdown();// 关闭线程池
                    }

});

继续爆出下面的问题

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

最终我使用ThreadPoolExecutor

            //构造一个线程池
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                            1,1,10,TimeUnit.SECONDS, 
                            new ArrayBlockingQueue<Runnable>(1),
                            new ThreadPoolExecutor.DiscardOldestPolicy());
            threadPool.execute(() -> {
                    try {
                            handlerMappingSummaryJobRunnable.run();//执行 方法
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally{
                    threadPool.shutdown();// 关闭线程池
                    }
                });

阿里爸爸终于不再教育我了。。。

下面来看一下构造器,了解一下参数含义

//     public ThreadPoolExecutor(
//  int corePoolSize, - 线程池核心池的大小。
//                              int maximumPoolSize, - 线程池的最大线程数。
//                              long keepAliveTime, - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
//                              TimeUnit unit, - keepAliveTime 的时间单位。
//                              BlockingQueue workQueue, - 用来储存等待执行任务的队列。
//                              ThreadFactory threadFactory, - 线程工厂。

//                              RejectedExecutionHandler handler)  - 拒绝策略。

由此开始简要概述一下创建线程的几种方式及优劣

new Thread 方式

优势:显而易见的线程创建方式

缺点:每次都要new对象,当有大量请求时,数不清new了多少个对象了,如果不及时关闭会导致内存溢出,因此还要考虑线程管理等问题。

Executors工厂创建线程池

newCachedThreadPool:

创建一个可缓存线程池

优点:很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建

缺点:一旦线程无限增长,会导致内存溢出。

newFixedThreadPool :

优点:创建一个固定大小线程池,超出的线程会在队列中等待。

缺点:不支持自定义拒绝策略,大小固定,难以扩展

newScheduledThreadPool :

优点:创建一个固定大小线程池,可以定时或周期性的执行任务。

缺点:任务是单线程方式执行,一旦一个任务失败其他任务也受影响

newSingleThreadExecutor :

优点:创建一个单线程的线程池,保证线程的顺序执行

缺点:不适合并发。。不懂为什么这种操作要用线程池。。为什么不直接用队列

统一缺点:不支持自定义拒绝策略。

通过ThreadPoolExecutor创建线程池

优点:集上述优点于一身

缺点:没发现缺点,因为上述线程池的底层就是通过它来创建的。。哈哈哈

上源码举个例子!

public static ExecutorService newFixedThreadPool(int nThreads) {         return new ThreadPoolExecutor(nThreads, nThreads,                                       0L, TimeUnit.MILLISECONDS,                                       new LinkedBlockingQueue());

}

你会发现Executors的底层就是ThreadPoolExecutor。。只不过加了很多限制,剩下的自己翻源码去!!