Java 并发包中线程池 ThreadPoolExecutor 原理探索

    线程池主要解决两个问题:
    一 是 执行大量异步任务 时,线程池能够提供较好的性能,没当需要执行异步任务时 直接 new 一个线程来运行,而线程的创建 和 销毁是需要开销的。线程池里的线程是 可复用 的,不需要每次执行异步任务时都重新创建和销毁线程。
     二 是 线程池提供了一种 资源限制 和 管理 的手段,比如可以限制线程的个数,动态新增线程等。每个 ThreadPoolExecutor 也保留了一些基本的统计数据,比如 当前线程池完成的任务数目等。
    另外,线程池提供了许多可调参数 和 可扩展接口,已满足不同情境的需要,用户可以使用更方便的 Executors 的工厂方法,比如 newCachedThreadPool (线程池线程个数最多可达 Integer.MAX_VALUE ,线程自动回收)、newFixedThreadPool (固定大小的线程池) 和 newSingleThreadExecutor(单个线程)等来创建线程池。

一、类图

在这里插入图片描述

    可以看到,Executors 其实是个工具类,里面提供了许多静态方法,这些方法根据用户选择 返回不同的线程实例。 ThreadPoolExecutor 继承了 AbstractExecutorService ,成员变量 ctl 是一个 Integer 的原子变量,用来记录线程池状态 和 线程池中线程个数,类似于 ReentrantReadWriteLock 使用一个变量来保存两种信息。mainLock 是 独占锁,用来控制新增 Worker 线程操作的原子性。termination 是该锁对应的条件队列,在线程调用 await(Termination) 时,用来存放阻塞的线程。
    Worker 继承 AQS 和 Runnable 接口,是具体承载任务的对象 。它继承了 AQS,自己实现了简单不可重入独占锁,其中 state = 0 表示 锁未被获取状态;state = 1 表示 锁已经被获取状态,state = -1 是创建 Worker 时默认的状态,创建时状态为 -1 是为了避免该线程运行 runWorker() 方法前被打断。Worker 中的变量 firstTask 记录该工作线程 执行的第一个任务,thread 是具体执行任务的线程。
    DefaultThreadFactory 是线程工厂,newThread 方法是对线程的一个修饰。其中 poolNumber 是个静态的原子变量,用来统计线程工厂的个数,threadNumber 用来记录每个线程工厂 创建了多少线程,这两个值也作为 线程池 和 线程 的名称的一部分。
    假设 Integer 类型是 32 位二进制表示,则 其中 高 3 位表示线程池状态,(后面 29 位用来记录线程个数), 默认是 RUNNING 状态,线程个数是0:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  • 线程个数掩码位数
        并不是所有平台的 int 类型都是 32 位,所以准确来说,具体平台下 Integer 的 二进制位数 -3 后的剩余位数 所表示的数 才是线程个数:
private static final int COUNT_BITS = Integer.SIZE - 3;
  • 线程最大个数(低 29 位)
        0001 1111 1111 1111 1111 1111 1111 1111 :
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
  • 获取高 3 位 运行状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
  • 获取低 29 位 线程个数
private static int workerCountOf(int c)  { return c & CAPACITY; }
  • 计算 ctl 新值
private static int ctlOf(int rs, int wc) { return rs | wc; }
  • 线程池状态
    • RUNNING:接受新任务 并且 处理阻塞队列里的任务。
      1110 0000 0000 0000 0000 0000 0000 0000
private static final int RUNNING    = -1 << COUNT_BITS;
    • SHUTDOWN:拒绝新任务 但是 处理阻塞队列里的任务。
      0000 0000 0000 0000 0000 0000 0000 0000
private static final int SHUTDOWN   =  0 << COUNT_BITS;
    • STOP:拒绝新任务 并且 抛弃阻塞队列里的任务,同时会中断正在处理的任务。
      0010 0000 0000 0000 0000 0000 0000 0000
   private static final int STOP       =  1 << COUNT_BITS;
    • TIDYING:所有任务都执行完 (包含阻塞队列里的任务)后,当前线程池活动线程数为 0,将要调用 terminated 方法。
      0100 0000 0000 0000 0000 0000 0000 0000
 private static final int TIDYING    =  2 << COUNT_BITS;
    • TERMINATED:终止状态。termianted 方法调用完成以后的状态。
      0110 0000 0000 0000 0000 0000 0000 0000
 private static final int TERMINATED =  3 << COUNT_BITS;
  • 线程池状态转换:
    在这里插入图片描述
  • 线程池参数
    • corePoolSize:线程池核心线程个数。
    • workQueue:用于保存等待执行的任务的阻塞队列,比如 基于数组的有界 ArrayBlockingQueue、基于链表的无界 LinkedBlockingQueue、最多只有一个元素的同步队列 SynchronizedQueue 以及 优先级队列 PriorityBlockingQueu。
    • maximunPoolSize:线程池最大线程数量。
    • ThreadFactory:创建线程的工厂。
    • RejectedExecutionHandler:饱和策略 / 拒绝策略。当队列满 并且 线程个数达到 maximunPoolSize 后采取的策略,比如 AbortPolicy 抛出异常、CallerRunsPolicy 使用调用者所在的线程来执行任务、DiscardOldestPolicy 调用 poll 丢弃一个任务,执行当前任务、 以及 DiscardPolicy 默默丢弃,不抛出异常。
    • keepAliveTime:存活时间。如果当前线程池里的线程数量 比 核心线程数 多,并且是闲置状态,则 这些闲置的线程 能存活的最大时间。
    • TimeUnit:存活时间的单位。
          
  • 线程池类型
    • newFixedThreadPool:创建一个核心线程个数 和 最大线程个数 都为 nThreads 的线程池,并且阻塞队列的长度为 Integer.MAX_VALUE。keepAliveTime = 0 ,说明 只要线程个数比核心线程个数多 且 当前空闲,则被回收。
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    • newSingleThreadExecutor:创建一个核心线程个数 和 最大线程个数 都为 1 的线程池,并且 阻塞队列长度为 Integer.MAX_VALUE。 keepAliveTime = 0,说明 只要线程个数比核心线程个数多 且 当前空闲,则被回收。
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    • newCachedThreadPool:创建一个 按需创建线程 的线程池,初始线程个数为 0 ,最多线程个数为 Integer.MAX_VALUE,并且阻塞队列为同步队列。 keepAliveTime = 60,说明 只要线程个数比核心线程个数多 且 当前空闲,在 60 s 内被回收。这个类型的特殊之处在于,加入同步队列的任务会被马上执行,同步队列里最多只有一个任务。
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    

二、源码分析

(1) public void execute(Runnable command)

     execute 方法的作用 是提交任务 command 到线程池进行执行。用户线程提交任务到线程池的模型图如下所示:
在这里插入图片描述
    可以看到,ThreadPoolExecutor 的实现 实际上是一个 生产消费模型,当用户添加任务到线程池时 相当于 生产者 生成元素,workers 线程工作集 中的线程 直接执行任务 或 从任务队列中获取任务 则相当于消费元素。
源码:

 public void execute(Runnable command) {
 		// 如果任务为 null,则抛出 NPE 异常
        if (command == null)
            throw new NullPointerException();
        	
        // 获取当前线程池的状态 和 线程个数 
        int c = ctl.get();

		// 如果当前线程池中 线程个数 小于 核心线程池size
        if (workerCountOf(c) < corePoolSize) {
        	// (一)就开启新线程运行,也就是 向 worker 中新增一个核心线程执行任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }

		// 如果如果当前线程池中 线程个数 大于等于 核心线程池size
        // 如果线程池处于 RUNNING 状态,则 添加任务到阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {

			// 二次检查
            int recheck = ctl.get();
            
            // 如果当前线程状态不是 RUNNING 则从队列中删除任务
            if (! isRunning(recheck) && remove(command))
            	// 并执行拒绝策略
                reject(command);

			// 如果当前线程池为空,则添加一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }

		// 如果队列满,则新增线程
        else if (!addWorker(command, false))
        	// 新增线程失败,说明任务队列已满,则执行拒绝策略
            reject(command);
    }

(一)addWorker :

   private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

			// 检查队列是否只在必要时为空
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

			// 循环 CAS 增加线程个数
            for (;;) {
                int wc = workerCountOf(c);

				// 如果线程个数超限则返回 false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;

				// 通过 CAS 增加线程个数,同时只有一个线程成功
                if (compareAndIncrementWorkerCount(c))

					// CAS 成功则退出双循环
                    break retry;

				// CAS 失败则重新获取线程状态
                c = ctl.get();  // Re-read ctl
                
                // 如果线程状态变化了,则再次进入外层循环 获取状态
                if (runStateOf(c) != rs)
                    continue retry;
                    
                 // 否则继续 CAS 尝试   
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

		// 代码走到这里说明 CAS 成功了
		
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        
        	// 创建 worker 
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
            	
            	// 获取独占锁
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    
                    // 重新检查线程池状态,以避免获取锁前调用了 shutdown
                    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);
                        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;
    }

    addWorker 方法比较长,分为两部分,第一部分 双重循环,目的是通过 CAS 操作增加线程数 ; 第二部分 主要是把并发安全的任务 添加到 workers 中,并且启动任务执行。
    注意到刚开始有一段:

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

    展开 ! 运算后等价于:

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

    也就是说,在 addWorker 方法中,以下情况会返回 false:

  • 当前线程池状态为 STOP、TIDYING 或 TERMINATED。
  • 当前线程池状态为 SHUTDOWN 并且 已经有了第一个任务。
  • 当前线程池状态为 SHUTDOWN 并且任务队列为空。
        
(2)工作线程 Worker 的执行

    用户线程提交任务到线程池后,由 Worker 执行。

Worker 的构造方法:

 Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

    可以看到,在构造方法里 首先设置 Worker 的状态为 -1,这是避免当前 Worker 在调用 newWorker 方法前被中断(当其他线程调用了线程池的 shutdownNow 时,如果 Worker 状态 >=0 则会中断该线程)。
    
run() 方法的源码:

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

(一):

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 (task != null || (task = getTask()) != null) {

			// 执行具体任务期间加锁,避免在任务运行期间,其他线程调用了 shutdown 后正在执行的任务被中断(showdown 只会中断当前被阻塞挂起的线程)
            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();
                } 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;

				// 统计当前 Worker 完成了多少个任务
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
    
    	// (二)执行清理工作
        processWorkerExit(w, completedAbruptly);
    }
}

(二) processWorkerExit:

  private void processWorkerExit(Worker w, boolean completedAbruptly) {
        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();
        }

		// 如果当前是 SHUTDOWN 状态 并且 队列为空,
		// 或者 当前是 STOP 状态 并且 当前线程池中没有活动线程 ,
		// 尝试设置线程池状态为 TERMINATED,
		// 如果设置成了 TERMINATED 状态,还会调用条件变量 termination.signAll() 方法
		// 激活所有因为调用线程池的 awaitTermination 方法而被阻塞的线程。
        tryTerminate();

        int c = ctl.get();

		// 判断当前线程池里线程个数是否小于核心线程个数,如果小于,则新增一个线程
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

    

(3) shutdown 操作

    调用 shutdown 操作后,线程池就不会再接受新的任务了,但是 工作队列里的任务还是要执行的。该方法会立刻返回,并不等待队列任务完成后再返回。
源码:

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	// 权限检查
            checkShutdownAccess();

			// (一)设置当前线程状态为 SHUTDOWN,如果已经是 SHUTDOWN 则直接返回
            advanceRunState(SHUTDOWN);
            
			// (二)设置所有空闲线程的中断标志
            interruptIdleWorkers();
            
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // (四)尝试将状态变为 TERMINATED  
        tryTerminate();
    }

    可以看到,shutdown 方法中,是先调用了checkShutdownAccess() 方法进行权限检查,在这个方法中, 看 是否设置了安全管理器,是 则看 调用 shutdowwn 命令的线程 是否有关闭线程的权限,如果有,还有看调用线程是否有中断工作线程的权限,如果没有权限,则抛出 SecurityException 或者 NullPointerException 异常。

(一) advanceRunState:

 private void advanceRunState(int targetState) {
    for (;;) {
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

    可以看到,传入的参数是 SHUTDOWN,那么 如果当前线程池状态 >= SHUTDOWN 就会直接返回,否则设置为 SHUTDOWN 状态。
(二)interruptIdleWorkers():

 private void interruptIdleWorkers() {
 		// (三)
        interruptIdleWorkers(false);
    }

(三):

private void interruptIdleWorkers(boolean onlyOne) {
		// 加锁,同时只有一个线程可以调用 shutdown 方法设置中断标志
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        
        try {
            for (Worker w : workers) {
                Thread t = w.thread;

				// 如果工作线程没有被中断,并且 没有正在运行 则设置中断标志
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

(四)tryTerminate():

 final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        terminated();
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

    可以看到,tryTerminate() 方法是先使用 CAS 设置当前线程池状态为 TIDYING,最后会调用 termination.signalAll(); 激活因为调用条件变量 termination 的 awaita() 系列方法而被阻塞的所有线程。
    

(4) shutdownNow 操作

     调用 shutdownNow 操作后,线程池就不会再接受新的任务了,并且会丢弃工作队列里的任务,正在执行的任务会被中断,该方法会立刻返回,并不等待激活的任务执行完成,返回值是这时队列里被丢弃的任务列表

源码:

  public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    	// 权限检查
        checkShutdownAccess();

		// 设置线程池状态为 STOP
        advanceRunState(STOP);

		// (一)中断所有线程
        interruptWorkers();

		// 将队列任务移动到 tasks 中
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

(一)interruptWorkers 中断所有线程,包括 空闲线程 和 正在执行任务的线程:

private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }

    

(5) awaitTermination 操作

     当线程调用 awaitTermination 方法后,当前线程会被阻塞,直到线程池状态变为 TERMINATED 才返回,或者等待时间超时才返回。

源码:

public boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException {
    long nanos = unit.toNanos(timeout);

	// 获取独占锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    
    try {
        for (;;) {
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
            if (nanos <= 0)
                return false;
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }
}

     可以看到,awaitTermination 方法中,先获取独占锁,然后在循环内部,会判断当前线程池状态是否至少是 TERMINATED 状态,如果是 则直接返回 true,否则 说明当前线程池中还有线程在执行,则 看设置的时间 nanos 是否小于 0,小于 0 则说明不需要等待,直接返回 false,如果大于 0 ,调用 termination.awaitNanos(nanos) ,期望在这段时间内 线程池状态会变为 TERMINATED。 当等待时间超时后,termination.awaitNanos(nanos); 就会返回,这时会继续循环,重新检查当前线程池状态是否为 TERMINATED ,如果是 直接返回,否则继续阻塞,挂起自己。
    

三、总结

     线程池巧妙地使用一个 Integer 类型的原子变量 来记录 线程池状态 和 线程池中的线程个数。通过 线程池状态 来控制任务的执行,每个 worker 线程可以处理多个任务,线程池通过线程复用 减少了 线程创建 和 销毁的开销。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值