Java 线程池ThreadPoolExecutor

本文详细解析Java线程池ThreadPoolExecutor的工作原理,包括线程池的创建、参数配置及内部运行机制。通过源码分析,揭示线程池如何管理线程、处理任务以及其状态变化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 线程池ThreadPoolExecutor

1、线程池的作用

我们清楚当某一个功能需要用到多线程的时候,传统的实现方法需要每次都去创建和销毁线程,这样如果单位时间内线程数量很多,导致创建和销毁会暂用大量的时间和空间,针对该问题,后续引出了线程池,线程池的作用就是能统一把线程放到一起管理,并且用几乎很少的线程创建和销毁来管理一堆的线程。

2、线程池

线程池的创建

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
这里也就举出一个方法的例子来进行之后的讲解吧,我们可以看出,Executors只是个工厂而已,方法也只是来实例化不同的对象,实际上实例化出来的关键类就是ThreadPoolExecutor。现在我们就先来简单地对ThreadPoolExecutor构造函数内的每个参数进行解释一下吧。

corePoolSize(核心线程池大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,当任务数大于核心线程数的时候就不会再创建。在这里要注意一点,线程池刚创建的时候,其中并没有创建任何线程,而是等任务来才去创建线程,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法 ,这样才会预先创建好corePoolSize个线程或者一个线程。

maximumPoolSize(线程池最大线程数):线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界队列,此参数就没有意义了。

keepAliveTime(线程活动保持时间):此参数默认在线程数大于corePoolSize的情况下才会起作用, 当线程的空闲时间达到keepAliveTime的时候就会终止,直至线程数目小于corePoolSize。不过如果调用了allowCoreThreadTimeOut方法,则当线程数目小于corePoolSize的时候也会起作用.

unit(keelAliveTime的时间单位):keelAliveTime的时间单位,一共有7种,在这里就不列举了。

3、源码

    //初始化-536870912。存放线程池的有效线程数和线程池的运行状态
    /**
     * In order to pack them into one int, we limit workerCount to
     * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
     * billion) otherwise representable. If this is ever an issue in
     * the future, the variable can be changed to be an AtomicLong,
     * and the shift/mask constants below adjusted. But until the need
     * arises, this code is a bit faster and simpler using an int.
     */
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //线程池状态码
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
 
    // runState is stored in the high-order bits
    //可以看出状态码是-1 0 1 2 3 位移29位
    //可以看出running的状态小于0 -1位移29位为负整数,每次添加一个线程,做++操作。
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
 
    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }
 
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        //AtomicInteger存
        int c = ctl.get();
        //工作线程小于corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            //添加一个core线程,此处参数为true,表示添加的线程是core容量下的线程
            if (addWorker(command, true))
                return;
            //刷新数据,乐观锁就是没有锁
            c = ctl.get();
        }
        //判断线程池运行状态,工作队列是否有空间
        if (isRunning(c) && workQueue.offer(command)) {
            //再次检测
            int recheck = ctl.get();
            //线程池已停止,就移除队列
            if (! isRunning(recheck) && remove(command))
                //执行拒绝策略
                reject(command);
            //线程池在运行,有效线程数为0 
            else if (workerCountOf(recheck) == 0)
                //添加一个空线程进线程池,使用非core容量线程
                //仅有一种情况,会走这步,core线程数为0,max线程数>0,队列容量>0
                //创建一个非core容量的线程,线程池会将队列的command执行
                addWorker(null, false);
        }
        //线程池停止了或者队列已满,添加maximumPoolSize容量工作线程,如果失败,执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }
 
    final void reject(Runnable command) {
        handler.rejectedExecution(command, this);
    }

不管是ThreadPoolExecutor还是spring boot所提供的ThreadPoolTaskExecutor最终都是继承了Executor,并且execute以及submit的方法最后都是去执行Executor里面的execute方法。
上面的步骤中大致为:
1) 检查core线程池数量<corePoolSize数量,是,可以提交任务或新建线程执行任务 。

2)如果corePoolSize线程数量已使用,如果队列容量未满,则加入队列。

3)队列已满,创建maximumPoolSize线程数量执行;如果失败则执行关闭线程池或者拒绝策略。

在上面得代码中有一个addWorker方法,这个方法里面有一个Worker对象,在该对象里面其实就是重新实现了execute方法。
而在线程池中用到的submit方法只不过是在调用的execute方法的基础上,只是多了newTaskFor,用来收集线程的运算结果。

总结

1)线程池优先使用corePoolSize的数量执行工作任务

2)如果超过corePoolSize,队列入队

3)超过队列,使用maximumPoolSize-corePoolSize的线程处理,这部分线程超时不干活就销毁掉。

4)每个线程执行结束的时候,会判断当前的工作线程和任务数,如果任务数多,就会创建空线程从队列拿任务。

5)线程池执行完成,不会自动销毁,需要手工shutdown,修改线程池状态,中断所有线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值