我是靠谱客的博主 舒适大门,最近开发中收集的这篇文章主要介绍ThreadPoolExecutor 源码分析 - execute() 方法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

前面的两篇文章为分析 线程池源码 做了些铺垫, 主要内容是描述线程池的工作原理, 本文将通过分析源码的方式, 去验证线程池的工作原理。

Java 线程池 ThreadPoolExecutor 中的位运算操作

线程池 ThreadPoolExecutor 源码分析基础 - 线程池工作原理

先回顾下前面讲过的内容概要:

  • 线程池的几种状态 RUNNINGSHUTDOWN
  • 线程池状态间的切换
  • 线程池的核心参数 corePoolSizemaximumPoolSize
  • 线程池的工作原理 核心线程 -> 阻塞队列 -> 非核心线程 -> 拒绝策略
  • 如何判断线程池的状态和线程数量:通过 AtomicInteger ctl 变量的高低位判断
  • Worker 对象的作用

当掌握了以上知识点后, 再去分析具体源码就容易得多了。

正文

正文大纲

  • ThreadPoolExecutorworkers 属性
  • execute 方法
  • addWorker 方法
  • runWorker 方法
  • processWorkerExit 方法

ThreadPoolExecutor#workers 属性

线程池中有一个 workers 集合,里面记录了所有的工作线程,只有拿到 mainLock 锁的线程才能访问

/**
 * Set containing all worker threads in pool. Accessed only when
 * holding mainLock.
 */
private final HashSet<Worker> workers = new HashSet<Worker>();


// 该属性负责记录 workers 集合的长度, 也是需要拿到锁的线程才能访问
private int largestPoolSize;

ThreadPoolExecutor#execute(Runnble command) 方法

上一篇文章 线程池 ThreadPoolExecutor 源码分析基础 - 线程池工作原理
大致分析了 execute 方法的执行流程, 这里再次提一次。

了解一些小知识点:(前面文章也详细介绍过)

  • ctl.get() 同时记录了线程池状态和线程个数
  • workerCountOf 为线程池的线程数量, 取得就是 ctl.get() 的后几位
  • addWorker 暂时可以理解为开启一个线程, 下面会详细讲
  • isRunning(c) 判断线程池是否处于运行的状态
  • reject 表示拒绝新添加的任务
public void execute(Runnable command) {
	// 参数校验
    if (command == null)
        throw new NullPointerException();
        
    // 从 c 的值可以判断出线程池的状态, 以及线程池中线程的数量
    int c = ctl.get();
    // 1. 如果线程池的线程数量 小于 核心线程数
    if (workerCountOf(c) < corePoolSize) {
        // 添加一个核心线程 command 表示一个具体的任务, true 表示为核心线程
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2. 如果线程池处于 RUNNING 状态(只有处于此状态,才能接受新的任务)
    // 并且线程池的数量大于核心线程数, 就把任务添加到阻塞队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3. 如果队列也满了,就创建一个非核心线程(core==false)
    else if (!addWorker(command, false))
    		// 如果创建失败,就执行拒绝策略
        reject(command);
}

总结一下:

  1. 如果线程池的线程数量 小于 核心线程数, 添加一个核心线程 command
  2. 如果线程池处于 RUNNING 状态, 并且线程池的数量大于核心线程数, 就把任务添加到阻塞队列
  3. 如果队列也满了,就创建一个非核心线程
  4. 如果非核心线程创建失败,就执行拒绝策略

ThreadPoolExecutor#addWorker(Runnable, boolean)

该方法的作用暂时可以理解为:

创建并启动一个线程

该方法有两个参数:

  • Runnable firstTask
    如果该参数不为空, 那么通过 addWorker 方法创建的线程会先去执行 firstTask

  • boolean core
    core 的值与一个 if 语句有关系

    如果 core == true ,就把线程池的数量与 corePoolSize 的值作比较,
    反之, 与 maximumPoolSize 的值作比较。具体看源码分析

上面的 execute 中方法已经展示了 3 种 addWorker() 的调用方式

  • addWorker(firstTask, true)

  • addWorker(firstTask, false)

  • addWorker(null, false)

下面把 addWorker 方法分为两部分, 分割方法如下所示:

第一部分

该部分是个双层死循环

在死循环过程中,获取线程池状态, 判断线程池是否可以创建新的线程,或者是否可以执行阻塞队列中的任务。

该部分最终执行结果:

要么返回 false, 要么进入第二部分

而返回 false 有以下几种情况:

  • 线程池状态 > SHUTDOWN, 也就是不能接受新的任务,也不能处理队列中的任务
  • 线程池状态 == SHUTDOWN, 表示线程池可以处理队列中的任务,但是队列却为空
  • 或者线程池不能接受新的任务,但是 firstTask 却不为空
第二部分

如果顺利进入第二部分,就表示线程池可以创建新的线程

该部分的作用就是,通过 worker = new Worker() 创建 worker 对象,然后创建并开启线程,去执行 worker 对象的 run 方法。

private boolean addWorker(Runnable firstTask, boolean core) {
    // 一个标记, 类似 goto, 自行了解,结合下面的 break 和 continue 就能明白什么意思
    retry:
    // 第一部分
    for (;;) {
        int c = ctl.get();
        // 获取线程池状态
        int rs = runStateOf(c);
        
        // 返回 false 表示无法接收或处理任务
        // 如果不想返回 false,也就是 if 语句判断不成立,需要满足以下两个条件之一:
        // 1. rs < SHUTDOWN, 也就是 rs == RUNNING(我们之前讲过 RUNNING 表示可以接受新的任务)
        // 2. rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()
        // 	2.1 rs == SHUTDOWN 表示不会接收新的任务,但是会执行阻塞队列中的任务
        // 	2.2 firstTask == nul 表示要执行阻塞队列中的任务
        // 	2.3 阻塞队列不能为空
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN && firstTask == null &&  ! workQueue.isEmpty())
        )
               
            return false;
				
			
        for (;;) {
        		// 获取线程池中的线程数量
            int wc = workerCountOf(c);
            // 如果线程数量大于最大容量(CAPACITY), 直接返回 false
            // 如果 core == true ,就判断线程数量是否大于核心线程数
            // 如果 core == false, 就判断线程数量是否大于最大线程数
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
                
            // 通过 CAS 操作, 把线程池中的线程数量 + 1,然后跳出双层循环,进入第二部分
            if (compareAndIncrementWorkerCount(c))
                break retry;
                
            // CAS 操作失败,就核对线程池的状态
            c = ctl.get(); 
            if (runStateOf(c) != rs)
                // 继续从最外层循环开始执行
                continue retry;
            
        }
    }

	// 第二部分
	// 走到这里,表示线程池可以创建新的线程,或者可以执行队列中的任务
	// 两个标记,见名知义
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    
    try {
    	// 下面两行代码上一篇文章介绍 Worker 对象时,详细说明过
    	// 包装需要执行任务(注意 firstTask 可能为 null)
        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());
				// 如果线程池状态为 RUNNING
				// 或者
				// 如果线程池状态为 SHUTDOWN 且 firstTask == null,
                if (rs < SHUTDOWN ||  (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) 
                        throw new IllegalThreadStateException();
                        
                    // 添加至 workers 集合
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    // 添加成功
                    workerAdded = true;
                }
            } finally {
                // 释放锁
                mainLock.unlock();
            }
            
            // 启动线程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
    	// 如果线程启动失败, 就删除 workers 集合中刚添加的 Worker 对象
        if (! workerStarted)
            addWorkerFailed(w);
    }
    
    return workerStarted;
}

Worker#run

run 方法中调用 runWorker方法

public void run() {
    runWorker(this);   this 表示具体的 worker 对象
}

ThreadPoolExecutor#runWorker

addWorker 方法源码的第二部分中,会先通过 new Worker(firstTask) 创建 worker 对象,此时:

  • 传入的参数firstTask != null 时 , 表示线程启动后第一个执行的任务就是 firstTask
  • 而传入的参数firstTask == null 时 ,上面也讲了 firstTask == null 仅仅表示要求线程池去执行阻塞队列中的任务。

然后拿到 worker 对象的 thread 去启动线程, 最后执行 runWorker 方法

简要概括:

runWorker 方法会先去执行addWorker 时传入的 firstTask, 如果 firstTask == null, 就去执行阻塞队列中的任务

当两者都为空时, runWorker 方法执行结束。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    // 注意这一步把 firstTask 重新赋值给了 task 
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    		
    		// 1. task !=null, 就直接执行 task
    
    		// 2. task == null, 表示调用 addWorker 方法时传入 的 firstTask == null
    		// 			所以就通过 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 {
                    // 执行
                    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);
    }
}

Worker#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;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
	// 尝试设置线程池状态为 TERMINATED,
    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);
    }
}

总结

分析 ThreadPoolExecutor 源码前, 需要了解以下内容:

  • 线程池的数量和状态是由 Integer.SIZE - 3 个 2 进制位表示的, 其中高 3 位 表示线程池状态,其余低位表示线程个数

  • 获取线程池数量和状态都是基于上面的二进制位来计算的

    例如:

	private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;

    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
  • 线程池的几种状态, 以及状态之间如何切换的

  • 线程池 Worker 对象的作用

    Worker w = new Worker(firstTask);
    Thread t = w.thread;
    t.start();
    线程运行时,会去执行 Worker#run 方法
    然后 run 方法会去调用 ThreadPoolExecutor#runWorker(); 方法
    如果 firstTask != null , runWorker 就会去执行 firstTask 的 run 方法
    如果 firstTask == null , runWorker 就会去执行阻塞队列中的任务
    

最后

以上就是舒适大门为你收集整理的ThreadPoolExecutor 源码分析 - execute() 方法的全部内容,希望文章能够帮你解决ThreadPoolExecutor 源码分析 - execute() 方法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部