笔记:关于利用ThreadPoolExcuter创建固定大小的线程池

1.简单介绍线程池的主要参数:

核心线程数(corePoolSize):
最大线程数(maximumPoolSize):
corePoolSize 线程数量的线程最大空闲时间(keepAliveTime):
时间单位(unit):
阻塞队列(BlockingQueue):
阻塞队列又分为有界队列(如LinkedBlockingQueue)和无界队列(如ArrayBlockingQueue);有界队列容易造成任务丢失,无界队列容易造成资源耗尽;

这几个参数是如何控制线程数量的?

  1. 如果运行线程数小于corePoolSize,即使当前有空闲线程,提交新任务时也会新建一个线程来运行;
  2. 如果运行线程数大于或等于corePoolSize且小于maximumPoolSize,新提交的任务就会入列等待,等待有空闲线程;如果队列已满,并且运行线程数小于maximumPoolSize,将会新建一个线程来运行;
  3. 如果线程数大于maximumPoolSize,新提交的任务将会根据拒绝策略来处理。

ThreadPoolExecutor 定义了四种拒绝策略:

  • AbortPolicy:默认策略,在需要拒绝任务时抛出RejectedExecutionException;
  • CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务,如果线程池已经关闭,任务将被丢弃;
  • DiscardPolicy:直接丢弃任务;
  • DiscardOldestPolicy:丢弃队列中等待时间最长的任务,并执行当前提交的任务,如果线程池已经关闭,任务将被丢弃。

我们也可以自定义拒绝策略,只需要实现 RejectedExecutionHandler;需要注意的是,拒绝策略的运行需要指定线程池和队列的容量

2.为什么不直接使用Executors创建线程池

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
newFixedThreadPool()方法底层封装使用的还是ThreadPoolExcuter,底层原码如下:

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

new ThreadPoolExecutor()创建线程池:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

通过源码可以看出,用newFixedThreadPool()创建线程,核心线程数和最大线程数相等,阻塞队列为无界队列;因此该方式的弊端就是存在资源耗尽的风险;
通过ThreadPoolExcuter创建线程池,可灵活的控制核心线程数量、最大线程数量,阻塞队列以及拒绝策略,不建议使用无界队列;

本文参考以下链接,详见:
为什么阿里不允许用Executors创建线程池,而是通过ThreadPoolExecutor的方式?

3.创建线程池时踩的坑:

  1. 在方法内部创建线程池,而不是创建全局的线程池:
    在方法内部创建线程池(局部变量),每当方法被调用一次,就会创建一个新的线程池,如果线程没有及时关闭,最终可能会造成内存溢出;而创建全局变量,只有在java类被编译的时候才会创建线程池,这样大大减少了线程池创建的次数,也充分提现到了线程池的作用;一般用private static finnal修饰;
  2. 注意异常处理;
  3. 注意threadPoolExecutor.submit和executor的区别;
    未完待续。。。。。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值