线程池之 ThreadPoolExecutor

网上一堆 ThreadPoolExecutor 的解读,有些可能还相互矛盾,其实 ThreadPoolExecutor类的注释中就有大量的说明,本文基于jdk1.8.0中代码注释加上自己的一点理解与实践

一、为什么使用线程池

线程池主要解决2个方面的问题:

  1. 提升性能
    在执行大量异步任务时,由于减少了任务执行的开销而明显提升性能。并且在执行一系列任务集合时,线程池提供了一种限制并管理资源(包括线程在内)的一种方法。
    按我的理解,线程池能够重用之前的线程,甚至可以预创建线程,这就在实际任务执行时减少了创建线程的开销,进而提升性能。
  2. 线程池维护了一些基本的统计信息:例如已完成的任务数

二、线程池基本工作原理

我们先来看下创建线程池的基本参数
在这里插入图片描述

参数 说明
int coolPoolSize 核心线程数
int maxmumPoolSize 最大线程数
long keepAliveTime 存活时间
TimeUnit unit keepAliveTime参数的单位
BlockingQueue workQueue 线程池中的工作队列
threadFactory 线程工厂,负责创建线程
RejectedExecutionHandler handler 拒绝策略

1、 corePoolSize 和 maxmumPoolSize

线程池执行器会根据corePoolSize 和 maxmumPoolSize的值自动调整线程池的大小(getPoolSize: 获取线程池中当前线程数量)。
当一个新的任务提交(execute方法)过来时,如果线程池中的线程数小于corePoolSize时,创建一个新的线程来处理这个任务,哪怕其它工作线程是空闲的。如果线程池中的线程数大于等于corePoolSize并且小于maxmumPoolSize时,如果工作队列已经满了,创建一个新线程,如果工作队列没满,则新任务放入工作队列中。通过将maxmumPoolSize 设置为一个无边界的值(例如Integer.MAX_VALUE),线程池可以容纳任意数量的并发任务。通常情况下是通过构造函数来设置corePoolSize 和 maxmumPoolSize的,但是也可以通过setCorePoolSize() 和setMaxmumPoolSize()方法动态改变它的值

线程池工作原理示意图:

在这里插入图片描述

默认情况下,核心线程是只有在新的任务到来的时候才会创建并执行的,就是说线程池刚初始化的时候里面是没有任何线程的。但是可以通过调用prestartCoreThread 或者 prestartAllCoreThreads 方法来预先启动线程。

boolean  preStartCoreThread() 启动一个空闲的核心线程,等待任务到来。如果所有的核心线程都已经启动,返回false

int prestartAllCoreThreads() 启动所有的核心线程,并置为空闲等待任务到来。返回启动的线程数,也就是说如果之前已经启动了所有核心线程,调用该方法是不做任何操作的

代码示例

 public static void main(String[] args) throws Exception{
   
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 3L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10));
        boolean b = threadPoolExecutor.prestartCoreThread(); // 启动一个核心线程,成功
        System.out.println(b);
        b = threadPoolExecutor.prestartCoreThread();  // 启动一个核心线程,成功
        System.out.println(b);
        b = threadPoolExecutor.prestartCoreThread();// 启动一个核心线程,成功
        System.out.println(b);
        b = threadPoolExecutor.prestartCoreThread();// 启动一个核心线程,核心线程数为3,前面已经全部启动,这次失败
        System.out.println(b);
        System.out.println(threadPoolExecutor.getPoolSize());
        int i = threadPoolExecutor.prestartAllCoreThreads(); // 启动所有核心线程,因为之前已经启动了所有核心线程,返回0
        System.out.println(i);
        System.out.println(threadPoolExecutor.getPoolSize());
    }

执行结果: 
true
true
true
false
3
0
3

2、 keepAliveTime 和 unit

线程池里的线程数大于corePoolSize时,多余的线程在空闲超过keepAliveTime时间后将被终止。也可以用setKeepAliveTime方法来动态修改这个值。把这个值设置为Long.MAX_VALUE,TimeUnit.NANOSECONDS 可以有效防止在线程池关闭之前线程被终止。
上面强调是超过corePoolSize的多余线程才有用,也就是说默认情况下当线程池中线程数少于等于核心线程数时keepTimeAlive不会起作用。但是可以调用allowCoreThreadTimeOut(boolean)来将覆盖这种策略,前提是keepAliveTime>0。

我觉得需要注意的是:并没有核心线程这种东西,没有说核心线程数为3的线程池中最先启动的3条线程就是核心线程,后面启动的线程是非核心线程,只有核心线程数这个数量。以核心线程数为3,最大线程数为5,keepAliveTime=3秒为例的一个线程池,第一个启动的线程标记为t1,如果池中的线程数大于3条,并且t1的空闲时间超过3秒,那么t1将被终止,即使它是第一个启动的线程。不存在核心线程这种概念

我们做一个实验: 创建一个核心线程数为3,最大线程数为5,keepAliveTime=3秒,SynchronousQueue为工作队列的一个线程池。SynchronousQueue可以理解为一个转接器,有任务交给它,它立刻就转交给线程去执行

      ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 3L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        //threadPoolExecutor.allowCoreThreadTimeOut(true); 
        for(int i = 1; i<=4; i++) {
   
            final int j = i;
            threadPoolExecutor.execute(new Runnable() {
   
                @Override
                public void run() {
   
                    System.out.println(j+": "+Thread.currentThread().getName()+ " begin,this will last "+j+"s");
                    try {
   
                        Thread.sleep(j * 1000);
                    } catch (Exception e) {
   
                    }
                    System.out.println(j+": "+Thread.currentThread().getName()+ " end");
                }
            });
        }
        Thread.sleep(1100L); // 确保任务线程先执行完,主线程等待时间略加了一点
        System.out.println("1 秒以后PoolSize: "+threadPoolExecutor.getPoolSize());
        System.out.println("1 秒以后ActiveCount: "+threadPoolExecutor.getActiveCount());
        Thread.sleep(1000L);
        System.out.println("2 秒以后PoolSize: "+threadPoolExecutor.getPoolSize());
        System.out.println("2 秒以后ActiveCount: "+threadPoolExecutor.getActiveCount());
        Thread.sleep(1000L);
        System.out.println("3 秒以后PoolSize: "+threadPoolExecutor.getPoolSize());
        System.out.println("3 秒以后ActiveCount: "+threadPoolExecutor.getActiveCount());
        Thread.sleep(1000L);
        System.out.println("4 秒以后PoolSize: "+threadPoolExecutor.getPoolSize());
        System.out.println("4 秒以后ActiveCount: "+threadPoolExecutor.getActiveCount());
        Thread.sleep(1000L);

        System.out.println("5 秒以后PoolSize: "+threadPoolExecutor.getPoolSize());
        System.out.println("5 秒以后ActiveCount: "+threadPoolExecutor.getActiveCount());

        threadPoolExecutor.execute(new Runnable() {
   
            @Override
            public void run() {
   
                System.out.println("5: "+Thread.currentThread().getName()+ " begin,this will last 5s");
                try {
   
                    Thread.sleep(5 * 1000);
                } catch (Exception e) {
   

                }
                System.out.println("5: "+Thread.currentThread().getName()+ " end");
            }
        })
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值