线程池提交流程

线程池的提交流程

一、必备知识

需要知道线程池的核心参数,以及具体作用

int corePoolSize,    核心线程数
int maximumPoolSize,	最大线程数
long keepAliveTime,		最大存活时间
TimeUnit unit,		时间单位
BlockingQueue<Runnable> workQueue,		任务队列
ThreadFactory threadFactory,		线程工厂
RejectedExecutionHandler handler   	拒绝策略

// 线程池的构造函数可以体现
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
二、源码分析前需要知道的概念

首先两个问题:
1、如何控制线程池的状态?
2、如何控制线程数?

在jdk线程池源码中使用了一个变量ctl来进行控制,

// 用来控制线程池状态、数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

AtomicInteger 内部是一个int型value值进行保存,每次的获取状态信息、获取线程数信息都是取这个数;

那如何存储?
我们知道int型占4个字节,32个位,jdk把这32个位的前3位用来保存线程池的状态,后29位保存线程池的数量
在这里插入图片描述

源码如下:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 29
    private static final int COUNT_BITS = Integer.SIZE - 3;   
    // 容量 (1 << 29) - 1 == 536870911; 
    // 二进制位图:0001 1111 1111 1111 1111 1111 1111 1111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;   

    /**
    *	1110 0000 0000 0000 0000 0000 0000 0000
    *	接受新任务并处理排队的任务
    */
    private static final int RUNNING    = -1 << COUNT_BITS;
    /**
    *	0000 0000 0000 0000 0000 0000 0000 0000
    *	不接受新任务,而是处理排队的任务
    */
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    /**
    *	0010 0000 0000 0000 0000 0000 0000 0000
    *	不接受新任务,不处理排队的任务,中断正在进行的任务
    */
    private static final int STOP       =  1 << COUNT_BITS;
    /**
    *	0100 0000 0000 0000 0000 0000 0000 0000
    *	所有任务都已终止,workerCount 为零,转换到状态 TIDYING 的线程将运行 terminate() 钩子方法
    */
    private static final int TIDYING    =  2 << COUNT_BITS;
    /**
    *	0110 0000 0000 0000 0000 0000 0000 0000
    *	已终止、已完成
    */
    private static final int TERMINATED =  3 << COUNT_BITS;

状态对应高位结果如下:

线程池状态高3位状态解释
RUNNING111接受新任务并处理排队的任务
SHUTDOWN000不接受新任务,而是处理排队的任务
STOP001不接受新任务,不处理排队的任务,中断正在进行的任务
TIDYING010所有任务都已终止,workerCount 为零,转换到状态 TIDYING 的线程将运行 terminate() 钩子方法
TERMINATED011已终止、已完成
位计算方法作用方法体内容
runStateOf(int c)主要获取高3位信息c & ~CAPACITY
workerCountOf(int c)获取低29位信息c & CAPACITY
ctlOf(int rs, int wc)初始化ctlrs
三、核心类

在这里插入图片描述
拒绝策略可以自己看看源码;主要是Worker类;

private final class Worker 
					extends AbstractQueuedSynchronizer 
					implements Runnable
    {
        
        /** 维护的线程,从线程工厂中获取的*/
        final Thread thread;
        /** 执行的第一个任务,也是你execute(Runnable command)的任务,该任务执行完毕后会从队列获取 */
        Runnable firstTask;

        Worker(Runnable firstTask) {
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** worker启动后,会调用ThreadPoolExecutor的runWorker方法,启动线程去工作  */
        public void run() {
            runWorker(this);
        }
    }

四、提交流程源码分析 – 只摘取主要代码
4.1 任务提交
public void execute(Runnable command) {
		/** 线程池new出来的时候,ctl被init,
		ctl == ctlOf(RUNNING, 0) == RUNNING | 0 
		== 1110 0000 0000 0000 0000 0000 0000 0000
		所以状态高位111,为运行态,后面为0,线程数为0
		*/ 
        int c = ctl.get();
        // 取ctl低位为0,小于核心线程数,所以创建一个Worker加入线程集合;
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))   // addWorker见下面分析
                return;
            c = ctl.get();
        }
        // 如果线程池运行中,并且加入任务队列成功
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 再次检查,如果线程池未运行态,则移除任务
            if (! isRunning(recheck) && remove(command))
            	// 调用用户设置的拒绝策略处理任务
                reject(command);
            else if (workerCountOf(recheck) == 0)  //addWorker见下面分析
            	// 如果线程数为0,则创建一个新线程加入线程集合
                addWorker(null, false);
        }
        else if (!addWorker(command, false))  //addWorker见下面分析
        	// 尝试启动一个新线程去启动失败,调用拒绝策略处理任务;
            reject(command);
    }

1:当有新的任务添加时,如果线程数小于核心线程数,直接通过线程工厂获取线程,封装成Worker启动线程;
2:当前程数超过核心线程数,先判断线程池状态并将任务加入队列,如果线程现在是非运行态,则移除队列任务,调用拒绝策略来处理;
3:当前线程数超过核心线程数,先判断线程池状态并将任务加入队列,如果加入队列失败,则启动一个线程尝试处理,如果失败则调用拒绝策略;

4.2 添加线程到线程集合 --HashSet workers
/**
core 是否核心线程
*/
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 状态大于等于SHUTDOWN,可能STOP、TIDYING、TERMINATED,则不接受任务
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;
			// 无线循环获取线程数
            for (;;) {
                int wc = workerCountOf(c);
              	// 线程数过大加入失败
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                	// 线程数自增加1跳出循环
                    break retry;
                c = ctl.get();
                if (runStateOf(c) != rs)
                	// 线程池状态变化重试
                    continue retry;
                // 自增加1失败,再次循环
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask); // 将提交的任务封装成Worker,并从线程工厂内获取线程赋值给Worker,见下文
            final Thread t = w.thread;   // Worker的线程
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                   	// 获取运行状态
                    int rs = runStateOf(ctl.get());
					// 线程是运行态或者关闭态但是任务为null
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // 线程已经激活报错
                            throw new IllegalThreadStateException();
                        workers.add(w);  // 将线程加入线程集合
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;   // 线程加入成功
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                	// 如果加入成功,则启动线程
                    t.start();
                    workerStarted = true;  // 线程启动成功
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

1:首先判断状态大于等于SHUTDOWN,可能STOP、TIDYING、TERMINATED,则不接受任务;
2:无线循环修改ctl自增1,代表线程数要加1;
3:将Runnable firstTask, 封装成Worker(线程在构造中通过线程工厂获取的), 放入Workers集合;
4:启动Worker中的线程,

4.3 Worker分析
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {      
    	// Worker维护的线程
        final Thread thread;
		/** 
		执行的第一个任务,也是你execute(Runnable command)的任务,该任务执行完毕后会从队列获取 */
        Runnable firstTask;

        Worker(Runnable firstTask) {
            this.firstTask = firstTask;
            // 从线程工厂中获取线程,你可以自己设置线程工厂
            this.thread = getThreadFactory().newThread(this);   // this交给线程工厂,线程工厂把Worker交给了Thread,因为Worker实现了Runnable,看下文,4.6 
        }

        public void run() {
            runWorker(this); // Worker中维护的线程启动后会调用此方法;
        }
    }

1:调用ThreadPoolExecutor的runWorker方法启动线程;

4.4 runWorker获取队列任务分析
final void runWorker(Worker w) {
		// 获取Worker中的线程和任务
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        	// 如果任务不为空,则直接执行;如果任务为空,则从队列获取任务并执行
            while (task != null || (task = getTask()) != null) {
                w.lock();
                try {
                	// 类似于拦截器,执行前拦截,这是个protected方法,用于扩展,你可以自己实现
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                       // 类似于拦截器,执行后拦截,这是个protected方法,用于扩展,你可以自己实现
                    }
                } finally {
                	// 当前任务执行完毕赋值为空,等获取到新任务继续赋值
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
        	// 队列为空获取不到任务跳出While,以及线程处理task出错,线程退出;
            processWorkerExit(w, completedAbruptly);
        }
    }

1:首先获取Worker中的firstTask,并执行;
2:无线循环从队列中获取任务并执行,如果任务获取为空,则跳出循环,当前线程执行完毕;

4.5 getTask 从队列获取任务分析
    private Runnable getTask() {
    	// 获取任务是否超时
        boolean timedOut = false; // Did the last poll() time out?

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

            // SHUTDOWN 后续状态或者工作队列为空,则把线程减少至0;
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // 线程数大于核心线程数
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			// 线程数超过最大线程数,并且上次拉取任务超时了,你设置的最大存活时间
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                	// 将线程数减1,返回任务为空
                    return null;
                continue;
            }

            try {
            	// 根据你设置的最长等待时间来获取任务,
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 
                    workQueue.take();
                if (r != null)
                	// 将任务返回
                    return r;
                // 超过最长存活时间
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

1:如果线程池状态大于SHUTDOWN或者队列isEmpty,则返回空;
2:如果线程数大于核心线程数并且获取任务超时,则线程数减1,返回空;
3:从队列中获取任务,传入等待时间,不穿时间参数则长时间获取,就是Park;

4.6 Executors.defaultThreadFactory() 线程工厂

前面有Worker中维护了task和线程,而线程是从线程工厂中获得;但是Worker的Run是怎么启动的呢?

// 默认的线程工厂
public static ThreadFactory defaultThreadFactory() {
    return new DefaultThreadFactory();
}
static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public Thread newThread(Runnable r) {
       		// 主要看这里的r, r是Worker,而Worker的Run是线程启动调用的;
            Thread t = new Thread(group, r,  // 这里r传入了线程中,所以线程启动的时候这里会执行Worker的run方法;
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

至此就分析完毕了;
总结:
1:当有新的任务添加时,如果线程数小于核心线程数,直接通过线程工厂获取线程,封装成Worker启动线程;
2:当前程数超过核心线程数,先判断线程池状态并将任务加入队列,如果线程现在是非运行态,则移除队列任务,调用拒绝策略来处理;
3:当前线程数超过核心线程数,先判断线程池状态并将任务加入队列,如果加入队列失败,则启动一个线程尝试处理,如果失败则调用拒绝策略;
4:启动新线程如果线程池状态是RUNNING并且线程数或者超过最大线程数都会启动新线程失败;
5:启动得新线程会先执行firsttask,执行完毕之后去队列获取新任务,获取新任务的时候如果第一次获取任务超时,并且线程数大于核心线程数则返回任务为空,进行线程退出,线程数减1;

结尾

keepAliveTime只对超过核心线程数的线程有作用;
能力有限,有错误的地方多谢指正,有错误既更新;

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值