java线程池源码详细分析(ThreadPoolExecutor)

前置工作

一般我们使用的java线程池的大多都是由ThreadPoolExecutor所生成的。
类结构图如下:
在这里插入图片描述
在说源码前,先说明几个概念,这几个概念不弄明白,那源码也很难看懂。
首先是线程池的状态和数量在线程池中是如何表示的
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量
在这里插入图片描述
在线程池中使用了一个AtomicInteger类型的变量来保存线程池的状态和数量
不妨看看源码是如何定义的:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//初始值为-1
private static final int RUNNING    = -1 << COUNT_BITS;//RUNNING转换成二进制
//就是11100000000000000000000000000000,如果只看前面三位的话RUNNING的值就是-1
private static final int COUNT_BITS = Integer.SIZE - 3;//COUNT_BITS==29
private static int ctlOf(int rs, int wc) { return rs | wc; }

这个应该还是很好理解的吧,经过计算ctlOf(RUNNING, 0),初始化的ctl其实就是11100000000000000000000000000000,这个值表示的是线程池为RUNNING状态,0条工作线程。再举个例子:11100000000000000000000000000011,表示RUNNING状态,3条工作线程。

ctl的几个状态:

//下面的值只显示其二进制的前三位
private static final int RUNNING    = -1 << COUNT_BITS;//111
private static final int SHUTDOWN   =  0 << COUNT_BITS;//000
private static final int STOP       =  1 << COUNT_BITS;//001
private static final int TIDYING    =  2 << COUNT_BITS;//010
private static final int TERMINATED =  3 << COUNT_BITS;//011

//其中
private static final int COUNT_BITS = Integer.SIZE - 3;//29

好了,接下来就是最有意思的源码环节了😏

源码分析

首先从构造器出发
线程池的构造器:

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

通过这个构造器,我们就可以创建我们想要的线程池。
构造器有几个参数

BlockingQueue:这是一个阻塞队列,如果这个不懂,可以先去了解一下。
篇幅关系,在这就详细不说明了。

ThreadFactory :线程工厂
就拿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;

    //构造方法
    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();

        //获取线程组
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();

        //线程名称
        namePrefix = "pool-" +
                poolNumber.getAndIncrement() +
                "-thread-";
    }

    //创建线程
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                namePrefix + threadNumber.getAndIncrement(),
                0);

        //设置线程为非守护线程
        if (t.isDaemon())
            t.setDaemon(false);
        //设置线程的优先级,其中Thread.NORM_PRIORITY是5(优先级的范围是1~10)
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

其实本质上就是创建一个Thread,只不过多了一些参数设置

RejectedExecutionHandler:拒绝策略,阻塞队列满了且线程数量达到maximumPoolSize会采取对应的拒绝策略
拒绝策略,jdk默认实现有4种,拿一个举例

public static class AbortPolicy implements RejectedExecutionHandler {

    public AbortPolicy() { }

    //直接抛异常
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                " rejected from " +
                e.toString());
    }
}

上面就是构造器的介绍

接下来是线程池的任务提交,这也是最核心的方法了

	//当我们提交一个任务时所调用的方法
    public void execute(Runnable command) {
        //任务为空
        if (command == null)
            throw new NullPointerException();

        //获取ctl(就是我们刚才介绍的那个原子变量,后面不再提示)
        int c = ctl.get();
        
	       
	    //判断工作中的线程数量<corePoolSize(核心线程数量)
        if (workerCountOf(c) < corePoolSize) {
        /*
         	workerCountOf方法:获取原子变量后面29位的值,也就是工作线程的数量
	        private static int workerCountOf(int c)  { 
	        	return c & CAPACITY; 
	        }
	        private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
	        所以CAPACITY的二进制:00011111111111111111111111111111
        */
            //直接将任务添加到工作线程所在的集合中,addWorker()方法后面会有介绍
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }

        //代码能到这里说明工作线程数量>=corePoolSize,那么就尝试添加到阻塞队列中。
        //如果线程池是RUNNING状态并且成功添加到阻塞队列中
        if (isRunning(c) && workQueue.offer(command)) {
			/*
				isRunning()方法:
				private static boolean isRunning(int c) {
			        return c < SHUTDOWN;//ctl状态可以看上面的表格
			    }
			*/
			
            //再次获取ctl,在并发环境下,可能之前获取的ctl已经发生改变
            int recheck = ctl.get();
            /*
	            如果线程池处于!RUNNING状态,则删除阻塞队列中刚才添加的任务
	            (只有RUNNING状态才能将任务添加到阻塞队列中)
	            其实我觉得这块做再次校验的目的是尽量保证当线程池被其它线程shutdown
	            后,阻塞队列就不能再添加任务了(按理来说shutdown状态,阻塞队列是不能
	            再添加任务的)
	            为什么说它是尽量保证呢,因为它也只能保证从第一获取ctl和第二次获取ctl
	            之间如果ctl发生改变,能及时做出相应的修改
	            但如果我在第二次获取ctl后,也就是int recheck = ctl.get()执行后,
	            突然线程池变成了shutdown的状态,那下面的条件就不会成立,
	            刚刚添加到阻塞队列中的任务就不会被移除了。当然发生这种情况的概率非常
	            小,即使发生了,无非就是多执行一个任务。
             */
            if (! isRunning(recheck) && remove(command))
                //删除成功则说明任务添加失败,直接走拒接策略
                reject(command);
            /*
                如果线程池已经停止了(不处于RUNNING状态,但处于SHOTDOWN状态)
                判断工作中的线程数量是否为0,为0则创建一个空任务
                这个是不是感到很疑惑??????我看了当初也是懵了。
                我也是看了后面的源码才焕然大悟:工作中的线程数量为0时,那么就没有线程
                去阻塞队列中取任务执行了,那么我们刚才添加到阻塞队列的任务就永远不会
                执行。所以我们添加了一个空任务的Worker(Worker可以执行多个任务,后面会说到)。
                那为何工作中的线程数量会为0?因为我们可能设置corePoolSize为0(比如
                Executors.newCachedThreadPool()),那么所有线程接
                在keepAliveTime时间内获取不到阻塞队列中的任务时,就会被回收。最终导
                致线程池中工作的线程数量为0
            */
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //上面if条件不成立,则可能是阻塞队列满了,那么我们直接将任务添加到工作线程集
        //合中(工作线程数超过corePoolSize就会将任务添加到阻塞队列中,阻塞队列满了就会
        //再次尝试将任务添加到工作线程中,但也不能超过maximumPoolSize)
        else if (!addWorker(command, false))
            //超过maximumPoolSize则添加失败,走拒绝策略
            reject(command);
    }

接下来是addWorker()方法:

	//firstTask是添加的任务,core来判断是核心线程还是最大线程,即corePoolSize和maximumPoolSize
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            //获取ctl
            int c = ctl.get();
            //获取线程池运行状态
            int rs = runStateOf(c);

            /*
                判断线程池是否还在运行
                其中rs>=SHUTDOWN表示线程处于非RUNNING状态

                ! (rs == SHUTDOWN &&
                            firstTask == null &&
                            ! workQueue.isEmpty())

                 上面这三行条件:是为了保证阻塞队列的任务在SHUTDOWN状态下也能被执行(创建空任务的原因)
             */
            if (rs >= SHUTDOWN &&
                    ! (rs == SHUTDOWN &&
                            firstTask == null &&
                            ! workQueue.isEmpty()))
                return false;

            /*
             	下面这段带代码的目的是将工作线程的线程数量+1(也就是ctl中的后29位)
            */
            for (;;) {
                //获取工作线程的数量
                int wc = workerCountOf(c);


                /*
                    工作线程数量超过规定大小则返回false

                    条件1:wc >= CAPACITY  判断工作线程的数量是否超过线程的最大容量
                        其中CAPACITY的定义:private static final int CAPACITY   
                        = (1 << COUNT_BITS) - 1;
                        COUNT_BITS的定义private static final int COUNT_BITS = 
                        Integer.SIZE - 3;
                        经过计算CAPACITY的值为00011111111111111111111111111111
                        (前面3为用来表示线程状态),经计算,也就是536,870,912
                    条件2:wc >= (core ? corePoolSize : maximumPoolSize)   判
                    断core是否为核心线程,core是方法参数

                 */
                if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //cas将ctl的值+1(工作线程数+1),并结束外层循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                //上面cas失败,并且两次读取的ctl的运行状态不一致(说明线程池的运行状
                //态发生了改变),则continue外层循环重试
                if (runStateOf(c) != rs)
                    continue retry;

            }
        }


        /*
        	上面代码将ctl中的工作线程数量+1,但只是数量+1,任务还没有被执行。
         	下面这段代码是将任务添加到工作线程的集合中
        */
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;//这个Worker结构后面会谈及到,先看一下大致结构
        /*
        	private final class Worker
		        extends AbstractQueuedSynchronizer
		        implements Runnable
    		{
		        final Thread thread;
		        Runnable firstTask;//任务
		        volatile long completedTasks;//完成任务的数量
		        
		        public void run() {
            		runWorker(this);
           		}
		   }

        */
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    //获取线程池运行状态
                    int rs = runStateOf(ctl.get());

					/*
	                    rs < SHUTDOWN 判断线程池是否关闭
	                    rs == SHUTDOWN && firstTask == null是为了保证阻塞队列
	                    的任务在SHUTDOWN状态下也能被执行(创建空任务的原因)
	                */
                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            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);
                /*
                	private void addWorkerFailed(Worker w) {
				        final ReentrantLock mainLock = this.mainLock;
				        mainLock.lock();
				        try {
				            if (w != null)
				                workers.remove(w);删除工作线程集合中的Worker
				            decrementWorkerCount();工作线程数量-1
				            tryTerminate();尝试将线程池变成TERMINATED状态
				        } finally {
				            mainLock.unlock();
				        }
				    }
                */
                
        }
        //返回启动状态
        return workerStarted;
    }

上面我们说完了任务提交的大概流程,但你有没有想过添加到阻塞队列中的任务好像并没有被执行,一个Worker执行完了任务又会怎么样?线程池是怎么做到一个线程可以执行多个任务,而不是执行一个任务就释放一个线程。还有就是线程池的异常处理,按理来说如果一个异常没有被捕获,线程就会挂掉,挂掉后线程池的线程数量就会减小,那么线程池是怎么保证线程的数量的呢。带着问题,我们继续。

在上面代码分析中,我们还去没有仔细去分析Worker这个类,源码来

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        
        private static final long serialVersionUID = 6138294804551838833L;

        
        final Thread thread;
 
        Runnable firstTask;

        volatile long completedTasks;

        Worker(Runnable firstTask) {
            setState(-1); 
            this.firstTask = firstTask;//任务
            this.thread = getThreadFactory().newThread(this);//创建一个线程,
            //注意这里传递的任务不是firstTask ,而是this,因为Worker类也是Runnable的
            //子类,因此线程启动时调用的方法是下面的run方法
        }
        //线程启动调用的方法
        public void run() {
            runWorker(this);
        }

       
    }

其实这里用到了一个设计模式,装饰器模式(你们注意一下Runnable,实现+组合,这不典型的装饰器模式么)。
可以看出,一个Worker对应一个Thread
还得我们在execute()方法中说很重要的一行代码的t.start()吗?这里调用的其实是Worker中的run方法,不是firstTask的run方法。
因此t.start()也就是执行下面代码

public void run() {
    runWorker(this);
}

我们再进入runWorker()方法

//这里我就不逐行解释了,就说了几行关键部分
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        /*
        	发现没有,while循环,一个Worker是可以执行多个任务的
        	task!=null,还记得execute()中的addWorker(null, false)这行代码吗?添加一个空任务
        	那行代码在这里就起作用了,如果任务为null,那么就会去阻塞队列中取任务
        	getTask()就是去阻塞队列中获取任务,如果没有任务getTask()会一直阻塞,
        	除非等待时间超过keepAliveTime或者线程池被终止运行,该方法后面会详细说到
        	getTask()后面会详细说到
        */
            while (task != null || (task = getTask()) != null) {
                w.lock();
                
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    	//这个任务才是我们之前execute()方法传递过来的任务
                        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);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
        	//Worker收尾工作,后面会详细说到
            processWorkerExit(w, completedAbruptly);
        }
    }

有了上面的这个方法,问题差不多解决了一半

我们看看getTask()方法到底做了些什么

private Runnable getTask() {

        //线程去阻塞队列中获取任务执行,如果阻塞队列一直没有任务,则会超时
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            //获取ctl
            int c = ctl.get();
            //获取线程池运行状态
            int rs = runStateOf(c);

            /*
                如果线程池为STOP、TIDYING、TERMINATED这几种状态,则该工作线程应该终止
                如果线程为SHUTDOWN状态并且阻塞队列为空,则该工作线程也应该终止
                简单说就是如果线程池关闭了,该工作线程就应该终止
            */
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                //工作线程数量-1
                decrementWorkerCount();
                return null;
            }


            int wc = workerCountOf(c);


            /*
                allowCoreThreadTimeOut:如果为false(默认值),核心线程即使处于空闲状态也保持活动状态。如果为true,核心线程使用keepAliveTime超时等待工作。
                    private volatile boolean allowCoreThreadTimeOut;

                timed可以理解为线程是否能一直处于空闲状态,线程处于空闲状态一段时间后会被回收(默认数量超过corePoolSize的线程需要被回收,
                如果设置allowCoreThreadTimeOut为true,那么创建的所有线程都不会被回收)
            */
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            /*
                这块代码目地是让那些等待超时的线程被回收

                这里的第一个条件: wc > maximumPoolSize 我也不知道开发人员为何要这样写,
                因为wc>maximumPoolSize是不可能发生的,addWorker()方法中已经保证了
                在并发环境下wc<=maximumPoolSize。
                第二个条件:timed && timedOut 目的是保证那些等待超时的线程被回收
                第三个条件:wc > 1 || workQueue.isEmpty() 保证线程池中至少有一个
                线程活动,除非阻塞队列为空

            */
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            /*
                timed为true则线程不能一直处于空闲状态,等待一定时间后还没接收到任
                务,那么线程就会被回收
                timed为false则线程一直等待阻塞队列中的任务。篇幅比较大,阻塞队列
                就不进行说明了。
                总之一句话poll()只等待一段的时间,而take()会一直等待
            */
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                //如果r不为null,则说明从阻塞队列中拿到了任务
                if (r != null)
                    return r;
                //代码如果能走到这里,则r为null,等待超时
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

最后就是Worker结束所调用的方法

//参数  w:工作线程   completedAbruptly:true表示工作线程异常,false表示工作线程正常执行
    private void processWorkerExit(ThreadPoolExecutor.Worker w, boolean completedAbruptly) {

        //如果是异常退出,则认为任务不计数,因为之前执行了incrementWorkerCount,所
        //以需要执行decrementWorkerCount
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //线程池完成的总任务
            completedTaskCount += w.completedTasks;
            //删除工作线程集合中的Worker
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        //尝试将线程池的状态改为TERMINATED
        tryTerminate();

        //获取ctl
        int c = ctl.get();


        /*
            private static boolean runStateLessThan(int c, int s) {
                return c < s;
            }
            判断运行状态是否为SHUTDOWN或RUNNING
        */
        if (runStateLessThan(c, STOP)) {
            //工作线程正常执行完
            if (!completedAbruptly) {
                /*
                    min表示线程池核心线程数量(你也可以理解成corePoolSize)

                    allowCoreThreadTimeOut:如果为false(默认值),核心线程即使处于
                    空闲状态也保持活动状态。如果为true,核心线程使用keepAliveTime
                    超时等待工作。
                    private volatile boolean allowCoreThreadTimeOut;

                */
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;

                //如果阻塞队列中还有任务,则核心线程数量不能为0
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;

                //如果工作线程数量>=min(线程池核心线程数量),则直接返回,否则还会有
                //最后一行代码的执行,因为要保证工作中线程数量>=corePoolSize
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            /*
            添加一个新线程worker,这里worker的添加有可能是因为workerCountOf(c) >= min,
            也有可能是因为上面条件!completedAbruptly==false(工作线程出现异
            常,出现异常我们就需要重新创建一个新的worker,
            这也就是为何我们使用线程池时不需要考虑因线程异常而导致线程数量减小的原因)
            */
            addWorker(null, false);
        }
    }

ThreadPoolExecutor线程池的工作流程大概就是这样,如果哪里有误欢迎评论指出!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值