Java 线程池中的线程复用、空闲退出原理

一、线程复用

1.简述
使用 new Thread() 创建的线程,再调用 start() ,运行完毕 run() 之后就结束了,而线程池中的线程之所以能够复用,是因为线程池中的工作线程 run() 内部是一个循环,会循环从阻塞队列中获取 Runnable 任务来执行,如果获取不到任务就会被阻塞队列阻塞住,进入空闲状态,等有任务需要线程池处理了,线程会被阻塞队列唤醒,然后处理任务,达到复用的效果。

2.原理分析如下
①.线程池中线程 Worker 的 run 方法是调用的 runWorker 方法

②.runWorker 方法中存在一个循环,在没有任务时【task==null】会尝试获取任务,调用 getTask方法


③.getTask 方法中会从阻塞队列中尝试获取 Runnable 任务而被阻塞,进入空闲状态

二、线程池空闲退出


1.简述
在使用 ThreadPoolExecutor 创建线程池时,会传入最大线程数【maximumPoolSize】、核心线程数【corePoolSize】,默认在空闲时核心线程不会退出,而多出 corePoolSize 的非核心线程会退出,其实通过设置 allowCoreThreadTimeOut 为 true,核心线程在空闲时也会退出,而线程退出的原理就是使用上述结论1中的阻塞队列完成的,线程在获取任务的时候,都会调用一个叫做 getTask的方法,在 getTask 内部,非核心线程获取任务时,调用的是阻塞队列的 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 方法,就是说在指定时间从阻塞队列获取任务数据,如果获取不到,就会导致getTask返回一个 null,然后工作线程就会因为这个返回值而退出循环,就结束了线程的运行,而剩余的核心线程在从阻塞队列中获取任务的时候,调用的是阻塞队列的 workQueue.task(),这个方法是不会超时的,只有获取到任务数据才会停止阻塞,所以核心线程空闲时就是被阻塞了,但不会结束,但如果设置了 allowCoreThreadTimeOut 为 true,那么所有线程都平等了,获取任务时调用的都是 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),指定时间内没有获取到任务,getTask 都会返回 null,都会退出。

2.原理分析如下:
①.调用 getTask 获取任务时,存在线程数量的判断,默认情况下,当线程数大于核心线程数时,从队列中获取任务的方法就是调用带有超时时间的poll 方法,在超时之后,getTask 返回 null。
【注:以下是非核心线程在空闲时,获取任务超时退出逻辑说明】

private Runnable getTask() {
        //1.默认超时标志为 false
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            //2.获取工作线程的个数
            int wc = workerCountOf(c);

            //3.allowCoreThreadTimeOut 这个值默认是 false,表示是否在核心线程空闲时关闭,如果设置 true 的话,那么所有线程
            //  从队列中获取任务时,调用的都会是阻塞超时方法 poll
            //4.当前工作线程数大于核心线程数时,wc > corePoolSize 为 true, timed 为 true,表示当前线程是非核心线程
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            //5.wc > maximumPoolSize 表示当线程数超出了最大线程数限制,需要关闭,所以就直接线程数减一,返回 null
            //7.timeOut 变为 true 之后,非核心线程就会走这里的逻辑,方法结束循环,返回 null 用于结束工作线程的运行
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                //6.timed 为 true,非核心线程这里会调用 poll 阻塞超时方法获取任务,在指定时间内没有获取到任务的话,timeOut 就会变为 true
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

②.getTask 返回 null 之后,worker 工作线程的循环就会结束,线程就会结束运行进入终止状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值