Executor执行框架源代码分析(三)——ThreadPoolExecutor

     ThreadPoolExecutor的主要是管理ThreadFactory创建的线程,Executor提交的任务。看一下ThreadPoolExecutor的继承图:

                                                                      


由上图可以看到,ThreadPoolExecutor是Executor和ExecutorService的一个实现。这个类也是整个Executor实现的核心,它的实现主要分为三个主要的部分:

1、核心线程池,管理了用于执行task的线程。

2、任务等待队列,当任务无法获得线程执行时,用于保存等待的task。

3、拒绝策略,当等待队列已满,无法存储新的task时,则采用提供的拒绝策略拒绝新的task。

这三个部分在ThreadPoolExecutor的构造方法中就有体现,构造方法如下:

 public ThreadPoolExecutor(   int corePoolSize,  //核心线程数量
                              int maximumPoolSize, //最大线程数量
                              long keepAliveTime, //超时时间
                              TimeUnit unit,  //超时时间的单位
							  BlockingQueue<Runnable> workQueue, //waiting task queue,用于保存等待的task
							  ThreadFactory threadFactory, //线程工厂是用户自定义的
							  RejectedExecutionHandler handler //拒绝策略
						  ) {}

一、核心线程池的实现

       ThreadPoolExecutor中提供了一种机制,该机制维护了一定数量的线程(corePoolSize)。对于新进入的task,这个机制可以根据实际情况作出四种反应:1、使用现有的线程执行task ;2、放入等待队列(workQueue); 3、建一个创新的程执线行task;4、直接拒绝。这个机制就是线程池。

       在ThreadPoolExecutor中,executor方法负责将task提交给ThreadPoolExecutor。接下来看一下executor方法的实现,了解一下线程机制是如何体现的。 executor方法源代码如下:

public void execute(Runnable command) { 
	/**
	 * 源代码阅读说明:
	 * 1、这里的command就是task;因为这里使用了命令模式,所以用command命名;执行task,task被包装成了一个work。
	 * 2、isRunning检查当前ThreadPoolExecutor是否是运行状态
	 * 3、workerCountOf会根据ThreadPoolExecutor的状态值获得当前正的正在执行的和等待的work数量。
	 *  这里先不用深究isRunning和workerCounterOf方法的实现。
	 * 4、addWork方法会新增一个线程,返回true表示新增成功,返回false表示新增失败
	 */
	if (command == null)
		throw new NullPointerException();
  
	int c = ctl.get();
	//判断当前正在运行的线程是否达到核心线程数
	//如果小于核心线程数,直接addWork
	if (workerCountOf(c) < corePoolSize) {  
		if (addWorker(command, true))
			return;
		c = ctl.get();
	}
	
	//运行的线程已经达到核心线程数
	//workQueue.offer(command)将task放入队列中
	//offer添加成功则放回true,反之是false
	if (isRunning(c) && workQueue.offer(command)) {
		int recheck = ctl.get();
		
		//放入队列成功,再次isRunning, 如果已经是非运行状态,则移除当前的task
		//调用reject拒绝当前任务
		if (! isRunning(recheck) && remove(command))
			reject(command);
		else if (workerCountOf(recheck) == 0) 
			addWorker(null, false); //再次调用addWork,注意addWork的参数
	}
	else if (!addWorker(command, false)) //放入队列失败,再次调用addWork
		reject(command);
}

上面的代码的执行流程大致如下:

1、execute方法接收到一个task(也就是command);

2、判断当前正在运行的线程数是否大于corePoolSize,如果小于corePoolSize,则直接新增线程运行task。

3、 当前正在运行的线程数大于corePoolSize,会将task放入workQueue。如果放入成功,则task就会等待执行。

4、如果放入workQueue失败,则会再次尝试addWork(注意,再次addWork时,第二个参数是false),如果再次addWork失败,则拒绝该task。

这个执行流程基本就是线程池的运行方式。这里的addWork方法返回true表示新增线程成功,反之则失败。接下来看一下addWork代码的实现,了解addWork何时返回true,何时返回false。代码如下:

private boolean addWorker(Runnable firstTask, boolean core) {

		/**
		 * 源代码阅读说明:
		 * 1、runStateOf返回ThreadPoolExecutor的运行状态。状态值分别是:
		 *    RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
		 * 2、boolean变量core表示是否使用核心线程
		 * 3、CAPACITY常量表示ThreadPoolExecutor所支持的最大线程数量,
		 * 这个值与corePoolSize和maxPoolSize都没有关系,是JDK本身做的限制
		 */
        retry: //循环退出标签
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c); 

            //检查当前状态是否是SHUTDOWN,SHUTDOWN状态将不会再接收task
			//这就是为什么调用shutdown方法后,将不会再接收task的原因
            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))
                    break retry; //符合新增线程的条件,通过标签退出循环,然后继续执行
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
		
	  //addWorker余下的部分代码是用于新增线程和启动线程,下文进行说明
	  ………………
    }

 wc >= (core ? corePoolSize : maximumPoolSize)说明:

        首先,这个判断的用途是判断当前的work数量是否大于corePoolSize或者maximumPoolSize。具体根据谁来判断,是有boolean值来决定的。

    其次,在executor方法中,第一次判断当前执行的work是否大于corePoolSize,如果小于,则会进行addWork(command,true);这里的true就是core的值,也就决定了这个判断的值。因为ThreadPoolExecutor是多线程运行的,所以在这里又进行了第二次校验。

        最后,   在executor方法中,再次进行addWork时,其调用形式是addWork(command,false);core的值是false,也就是说第二次addWork创建的线程之后,线程的数量threadCount是:corePoolSize < threadCount < maxmumPoolSize

接下来是addWork的第二部分代码:

private boolean addWorker(Runnable firstTask, boolean core) {

	 ………………

	boolean workerStarted = false; //是否已经启动
	boolean workerAdded = false; //是否已经添加进workers
	Worker w = null;
	try {
		w = new Worker(firstTask); //创建一个worker
		final Thread t = w.thread;  
		if (t != null) {
			final ReentrantLock mainLock = this.mainLock;
			mainLock.lock();
			try {
				// Recheck while holding lock.
				// Back out on ThreadFactory failure or if
				// shut down before lock acquired.
				int rs = runStateOf(ctl.get());

				if (rs < SHUTDOWN ||
					(rs == SHUTDOWN && firstTask == null)) {
					if (t.isAlive()) // precheck that t is startable
						throw new IllegalThreadStateException();
					workers.add(w); //将线程放入线程队列,workers是一个hasSet<Runnable>集合
					int s = workers.size();
					if (s > largestPoolSize)
						largestPoolSize = s;
					workerAdded = true; //修改添加的状态值
				}
			} finally {
				mainLock.unlock();
			}
			if (workerAdded) { //如果已经添加成功,则启动线程,并修改状态值
				t.start();
				workerStarted = true;
			}
		}
	} finally {
		if (! workerStarted) //没有启动的线程,则进行移除,addWorkerFailed方法会移除新创建的worker
			addWorkerFailed(w);
	}
	return workerStarted;
}

到这里,整个execute的提交过程基本已经完成了,但是仍然存在几个疑问:

1、workQueue等待队列中的task如何被执行呢?

2、限制了线程的数量,意味着就需要重用这些线程,那这些线程又是如何被重用的呢?

关于这两个问题的实现是在Worker这个类中,也就是new Worker(firstTask)创建出来的对象中。加下来看一下Worker的shixian

private final class Worker extends AbstractQueuedSynchronizer implements Runnable{

		/**
		 * 源代码阅读说明:
		 * 1、Worker类实现了AbstractQueuedSynchronizer接口,这里并不讨论AQS的实现
		 * 2、这里的代码只截取了一部分,详细的代码可以阅读原文
		 */
         
        final Thread thread;
      
        Runnable firstTask;
 
        volatile long completedTasks;
 
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this); //创建线程
        }

      
        public void run() {
			//在addWork中有一行t.start()启动线程的代
			//当线程启动时,runWorker方法将会被执行
            runWorker(this); 
        }
		…………
 }
 
  final void runWorker(Worker w) { //runWork方法的实现
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask; //取出worker中的task
		
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) { //这个循环判断就是用于获取workerQueue的task
                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 {
                        task.run(); //调用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 {
            processWorkerExit(w, completedAbruptly);
        }
    }
【task != null || (task = getTask()) != null】这个判断中,首先会判断task是否为Null,如果为null,就会调用getTask从workQueue中获取task。反之,则会执行当前的task。当task本执行完之后,会在finally中将task置为null,紧接着下一次循环就会从workQueue中取任务,依次循环 ,知道workQueue中的task被执行完成。这里的取task操作是多线程执行,所以workQueue必须是线程安全的。


       最后,在ThreadPoolExecutor的构造函数中还有一个参数keepAliveTime,线程的存活时间。keepAliveTime需要结合allowCoreThreadTimeOut的值一起使用。allowCoreThreadTimeOut表示是否允许核心线程超时,true表示允许核心线程超时,false表示核心线程不会超时失效。

       当allowCoreThreadTimeOut的值为true时,所有在ThreadPoolExecutor中的线程在达到超时时间keepAliveTime时,都会失效;反之,则只有corePoolSize< newThreadCount <maxmumPoolSize中的newThreadCount部分的线程会失效。

       也就是说,当ThreadPoolExecutor维护的线程数量超过corePoolSize的值,多余部分的线程是一定会超时失效的;而对于小于corePoolSize的线程是否会失效,则取决于allowCoreThreadTimeOut的值。关于超时线程失效的代码实现在getTask方法中,代码如下:

private Runnable getTask() {
	boolean timedOut = false; // Did the last poll() time out?

	for (;;) {  //死循环,不断获取workQueue中的worker
		int c = ctl.get();
		int rs = runStateOf(c);

		// ThreadPoolExecutor已经停止了,则所有的线程都应该退出了
		if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
			decrementWorkerCount();
			return null; //线程返回退出
		}

		int wc = workerCountOf(c);

		//当allowCoreThreadTimeOut为true时,或者当前执行的worker超过corePoolSize时
		//需要检查超时设置,因为:
		//wc > corePoolSize表示有超出的多余线程,多余的线程是有超时限制的
		//allowCoreThreadTimeOut表示所有的线程都有可能超时
		boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; 

		//timedOut:获取worker超时
		//这个校验判断,如果成立,则线程就会返回退出;反之,则会一直循环等待。
		//通过循环等待的方式,保证线程的有效
		if ((wc > maximumPoolSize || (timed && timedOut))
			&& (wc > 1 || workQueue.isEmpty())) {
			if (compareAndDecrementWorkerCount(c))
				return null; //线程返回退出,相当于超时失效
			continue;
		}

		try {
			Runnable r = timed ?
				workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
				workQueue.take(); //取出队列中的worker
			if (r != null)
				return r;
			timedOut = true;
		} catch (InterruptedException retry) {
			timedOut = false;
		}
	}
}

二、任务等待队列

      任务等待队列通常是一个BlockQueue阻塞队列:

      1、当运行的线程数少于corePoolSize时,对于新提交的task,会新增一个线程去执行task。

      2、当运行的线程数大于corePoolSize,小于maxmumPoolSize时,ThreadPoolExecutor会将新的task放入队列中。

      3、当新的task放入队列失败时,需要判断当前运行的线程数量是否超过maxmumPoolSize,如果大于maxmumPoolSize,则拒绝该task;如果小于maxmumPoolSize,则创建一个新的线程。

      通常情况下,等待队列有如下三种策略:

      1、直接传递,示例:SynchronousQueue。在这种策略下,队列不会持有任何的task,而是将task直接传递给线程执行。如果没有空闲线程,则创建线程。在执行有内部依赖(task之间相互有依赖)的请求时,这种策略可以避免死机或者死锁。此外,由于队列不持有task,因此ThreadPoolExecutor需要一个无界的maxmumPoolSize,以此来保证task不会被拒绝。此种策略不适合耗时的任务。

       2、无界队列,示例:LinkedBlockingQueue。这种策略是创建一个可以无限扩展的队列,因为ThreadPoolExecutor会优先将task放入队列中,在超时corePoolSize的情况下。所以,采用无界队列就意味着maxmumPoolSize将不再起作用。由于,task都被放入了队列中,也就要求各个task之间不能够相互依赖,否则将会出现死锁。

       3、有界队列,示例:ArrayBlockingQueue。算是上面两种策略的一种折中。可以通过maxmumPoolSize来限制系统资源的使用,以保证系统资源不会被过度消耗。队列的size和maxmumPoolSize的设置将会很重要;如果queueSize大,maxmumPoolSize小,会限制了系统的吞吐量;反之,queueSize小,maxmumPoolSize大;则可能导致资源耗尽。

三、拒绝策略

       当task的数量超过ThreadPoolExecutor所允许的值时,ThreadPoolExecutor就会拒绝该task。ThreadPoolExecutor有四种拒绝的方式:

        1、抛出RejectedExecutionException,策略类:ThreadPoolExecutor.AbortPolicy;

        2、由调用者本身的线程去执行改task,策略类:ThreadPoolExecutor.CallerRunsPolicy;

        3、丢弃当前的task,策略类:ThreadPoolExecutor.DiscardPolicy;

        4、丢弃队列头部的task,然后将当前的task加入队列中,策略类:ThreadPoolExecutor.DiscardOldestPolicy。

拒绝策略的源代码非常简单,一看就懂,这里就不赘述了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值