Java线程池源码分析

文章目录

  • 一.Java线程池原理分析
  • 二.Java线程池源码分析
    • 1.接口继承关系
    • 2.核心类ThreadPoolExecutor
      • 2.1 入口方法 execute -- 调度器
      • 2.2 添加线程方法 addWorker
      • 2.3 Worker 内部类
      • 2.4 核心方法 runWorker -- 发动机
      • 2.5 任务获取方法 getTask
    • 3.流程回顾

一.Java线程池原理分析

上一篇是 线程池原理分析,只是说明了其大概流程框架,这篇文章将对Java线程池的实现源码做一些分析。

二.Java线程池源码分析

1.接口继承关系

这就是JUC中线程池相关的接口和实现类关系图:
Java线程池源码分析_第1张图片

  • Executor:执行者接口,用来执行任务,准确的说,Executor提供了executor()接口来执行已经提交的Runnable任务对象。Executor存在的目的是提供一种将任务提交和任务如何运行分开的机制。Executor接口只包括一个函数接口:void executor(Runnable command);

  • ThreadPoolExecutor:继承自AbstractExecutorService类的具体实现类,具体实现了线程池的功能。

  • ScheduleExecutorService:是一个具有延时和周期执行功能的ExecutorService,其具体实现类就是SchedulePoolExecutor。

  • Executors:这是一个静态工厂类,通过静态工厂方法返回一些参数默认的ThreadPoolExecutor和SchedulePoolExecutor对象。

  • 其他都是一些抽象类和接口,就不做说明,毕竟Javaer,都懂。

综上所述:
所有的线程池实现都是基于Executor接口的,ThreadPoolExecutor是一个jdk自己实现的线程池,Executors相当于一个工具类,我们可以方便的从其中直接使用jdk实现的默认参数的ThreadPoolExecutor(但是不推荐直接使用这个工具类)

2.核心类ThreadPoolExecutor

根据前面的继承关系结构图,我们知道ThreadPoolExecutor类是JDK中实现的线程池类,根据上篇文章我们也知道,目前我们实际使用到的线程池,也是这个类的实例。

来看一下主要属性:
Java线程池源码分析_第2张图片
这里面有几个关键参数:

  1. corePoolSize:核心线程池的大小,线程池里本身是没有线程的,如果线程池当前持有的线程数小于corePoolSize,提交任务到线程池以后,线程池就会新建一个线程(Thread类),新建的线程并不会被结束,线程池会维护它,如果提交的任务超过了corePoolSize,就会进入阻塞式等待队列
  2. maximumPoolSize:等待队列满了以后,线程池会继续新建线程,而maximumPooolSize是最大的线程数量,超过这个值,再提交的任务就会进入线程池的拒绝策略中。

可以看到都是volatile类型的值,这里是为了配合cas操作来做到线程同步。

2.1 入口方法 execute – 调度器

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
    
    	// 这里需要特别说明变量clt,这是一个巧妙设计的状态码,可以根据位运算来获得线程池多个属性
        int c = ctl.get();
		// workerCount是工作线程数量
        if (workerCountOf(c) < corePoolSize) { // 小于核心线程池大小
            if (addWorker(command, true)) // 试图直接创建一个worker线程
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { // 提交到BlockQueue
		// 这里使用offer的原因是offer不会阻塞线程,而put会阻塞线程
		// 同时offer也使用了putLock保证了线程安全

			// 再次确认线程池状态
            int recheck = ctl.get();
			// 如果线程池已经关闭,则删除任务,并且执行拒接策略
            if (! isRunning(recheck) && remove(command))
                reject(command);

			// 如果线程池中任务数量为0,则尝试新建一个worker线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false)) // 阻塞队列满
            reject(command); // 添加失败则进入拒绝策略
    }

解析:executor方法是添加任务到线程池中的方法,它会分三种情况进行处理:

  1. 如果线程池中任务数量小于核心线程池大小,就会新建一个worker线程然后将任务提交到工作线程中去做
  2. 如果线程池中任务数量>= 核心线程池大小,就会添加到阻塞队列等待,同时会再次确认线程池状态,如果两次状态不一致,则删除该任务。
  3. 非以上两种情况,就会新建一个worker线程,如果线程池中任务数量超过了maximumPoolSize(线程池最大线程数量),就会执行拒绝策略。

可以看到,整个流程和我们之前说的一模一样。只是使用了多次检查标志位,CAS操作来确保线程安全的细节,需要注意。

这个方法是线程池的入口方法,非常关键,可以说是线程池的调度器

2.2 添加线程方法 addWorker

  private boolean addWorker(Runnable firstTask, boolean core) {
  
  		// 这里使用到了JUC中常用的CAS循环
        retry:
        for (;;) { 
            int c = ctl.get(); // 获取线程池信息值
            int rs = runStateOf(c); // 获取线程池运行状态

            // Check if queue empty only if necessary.
			// rs>= SHUTDOWN都是线程池无效的情况
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c); // 获取工作线程数量

				// 如果工作线程数量超过了限制,返回false
                if (wc >= CAPACITY || 
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;	
				// CAS增加c的值,失败则退出循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs) // 如果线程池状态与之前不同则跳转回retry
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        } // 以上操作都是针对线程池的状态码和运行状态的操作


		// 下面的操作是针对添加线程到线程池,提交(第一个)任务并且运行
        boolean workerStarted = false;
        boolean workerAdded = false;
        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是一个hashset,我们将新建的Worker对象添加
                        workers.add(w); 
                        int s = workers.size();
                        if (s > largestPoolSize) // 线程池中曾经有的Worker对象的最大数
                            largestPoolSize = s; 
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start(); // 启动一个线程,调用worker的run方法
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

这个方法的主要作用就是添加Worker线程到线程池,当然要注意其中对于线程池状态的判断,以及对于线程安全的控制手段。

这里我们注意到,线程池实际上添加一个线程,是创建了一个Worker对象,其实这个Worker对象就是线程池中的实际工作人员。

2.3 Worker 内部类

	// Worker内部类,注意这个类实现了Runnable接口,继承自AQS类
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread; 
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) { 
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
			// thread通过工厂模式获得,这样做的好处是可以保证线程的统一
			// 比如线程名字,优先级,是否为守护线程等等
			// 传入了this指针,这样thread对象线程执行自己的run方法
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) { 
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
}

Worker类是一个方便维护线程的类,一个Worker里包装了一个Thread,然后再由线程池同一维护一个HashSet

Worker里的核心方法是run,而run方法中的runWorker方法又是线程池处理Worker对象(或者说线程)的一个核心方法。

2.4 核心方法 runWorker – 发动机

   final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        // 获得传入的task对象,即提交到线程池的任务
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
			// getTask只有在这个任务需要被取消
			//(线程池关闭,超时,线程池中线程数量超最大值)的情况下
			// 才会返回null,其余情况下一律返回需要执行的task
            while (task != null || (task = getTask()) != null) {
                w.lock(); // 这里需要获取锁
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                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(); // 执行提交的任务
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
						// 可以看到,afterExecutr作用是获得任务运行中抛出的任何异常
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock(); // 解锁
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
}

这里需要注意,持续从阻塞队列中取得task的getTask()方法:getTask()返回null以后,就会进入finally代码块,也就是说只要getTask()返回null,那么这个worker线程就以及完成他的使命,可以销毁了!

可以看到,这个方法是线程池中真正执行提交任务的核心方法,可以说是线程池运的发动机

2.5 任务获取方法 getTask

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

        for (;;) {
			// 这里需要注意的一点细节是:如果线程池预热完成
			//(也就是说worker线程已经达到了coreSize以后),
			// 那么以后提交的所有线程,都会先进队列,
			// 然后再由getTask()方法获得到worker线程执行
            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;
            }
			// 以上,还是需要先判断线程池的状态

            int wc = workerCountOf(c);

            // Are workers subject to culling?
			// 判断是否需要超时控制
			// 依据是两个,我们下面再说这个依据
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

			// 如果超过最大线程数,或者有超时控制,则根据需要关闭工作线程
            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();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
 }

这个方法也是至关重要,**我们记得,线程池的调度器–execute方法,会把任务提交到阻塞队列中,而这个方法,就是不断地从队列中取出一开始提交的任务,交给worker线程执行任务,**同时也有很多判断策略,包括线程池的关闭以及超时策略,都跟他息息相关。

PS :
超时关闭线程的策略有两种:

  1. 默认情况下,如果设置了keepAliveTime(线程存活时长),那么对于corePool以外的线程,会进行超时关闭,但是corePool内的线程不会,他们还是会尝试getTask,或者被阻塞在take()方法上(阻塞队列没有任务)
  2. 如果设置了allowCoreThreadTimeOut为true,则会导致超时策略对于corePool内的线程生效,也就是说如果超时以后,corePool内的线程一样会关闭

其余情况getTask()返回null就是线程池关闭的情况了。

3.流程回顾

最后候我们结合源码再来看一下线程池的执行流程:

  1. 提交任务到execute方法,如果当前线程数小于corePoolSize,则addWorker(),调用Worker.thread.start()启动一个新的worker线程,worker线程runWorker()正式进入循环开始干活
  2. 继续提交任务到execute(),如果线程数大于corePoolSize,则进入阻塞Queue等待
  3. 如果阻塞队列满,则又开始addWorker(),直到线程达到maximumPoolSize
  4. worker线程会不断getTask(),从阻塞Queue取任务,同时有超时策略
  5. 如果阻塞Queue空,则take()方法将worker线程等待,直到又有任务被添加,worker线程才被唤醒(这是阻塞队列的活了,和线程池关系不大)
  6. getTask()会根据状态码判断一个worker线程是不是超时了,超时就返回null,这个worker线程就会自毁

当然,这里我们分析的都是核心方法,线程池中还有很多其他方法,这里就不一一分析了,JUC包源码可以说是可读性最高的源码,我们更重要的是获取其中的编程智慧和架构智慧!

你可能感兴趣的:(java多线程)