ThreadPoolExecutor源码解析

构造器参数解释

构造器方法

生活案例:

有个人卖秋裤,后来来了一笔订单,他招了3个长期工做。客户发现做的不错,后来又来了一大笔订单,发现人手不够,且也就这笔订单量大,招个长期工也不太合适,就招了几个临时工共同帮忙分担做完。后来在想着解雇临时工的时候,怕客户又来一笔订单,就让他们在工厂待一段时间再回去。后来发现订单越来越多,临时工招不来了,就拒绝一些订单。

参数解释

corePoolSize            核心线程数就是长期工数量
maximumPoolSize      最大线程数就是临时工+长期工的数量
keepAliveTime          线程存活时间就是解雇临时工在工厂待的时间
TimeUnit                     时间单位就是临时工待多少秒还是多少小时
workQueue                   工作队列,把任务添加到队列中
threadFactor             创建线程工厂的
handler                        拒绝策略

拒绝策略

AbortPolicy(默认拒绝策略)

一旦执行任务数超过最大线程数+任务队列容量,就会抛出异常
AbortPolicy

CallerRunsPolicy

一旦线程池执行超过的任务时,就由调用方主动去执行。
CallerRunsPolicy

举例:就好比产品给你安排个活,你看下所有人手里都有活,接不了,产品就说自己找些人去搞

DiscardPolicy

对于执行不了的任务不做任何处理,放着不管。一般不建议用这种拒绝策略。
DiscardPolicy

DiscardOldestPolicy

这个策略会把最早没执行完的任务取消掉,去执行新来的任务。
DiscardOldestPolicy

举例:产品跟你说这个活很急,手下人手里都有活,只能把不重要的活先推掉先干这个产品这个活。

ctl变量介绍

ctl是一个32位的int类型字段,其中高3位表示线程池的状态,由于线程池有5个状态,如果使用2位表示是不够的,所以使用高3位表示线程状态,低29位表示有效线程数量。
ctl

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits 高三位存储运行状态
    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

从这里看出高三位只有运行状态的为负数,其他都是正数,也就是ctl.get() < 0 代表着线程处于运行状态

execute()介绍

1.拿到ctl,根据ctl算出工作线程数
2.如果工作线程数小于核心线程数,添加核心线程
3.如果核心线程满了,则添加到任务队列。
4.如果任务队列满了,此时判断工作线程数是否大于最大线程数,如果小于,则添加非核心线程数,如果大于,直接执行拒绝策略。

public void execute(Runnable command) {
	# 校验任务是否为空
    if (command == null)
        throw new NullPointerException();
    // 获取ctl
    int c = ctl.get();
    // 判断工作线程数是否小于核心线程数
    if (workerCountOf(c) < corePoolSize) {
    	// 尝试添加核心线程,true代表核心线程,false代表非核心线程
        if (addWorker(command, true))
            return;
        // 添加核心线程失败,此时ctl值已经被修改了,所以需要重新获取
        c = ctl.get();
    }
    // 判断线程池状态是否是运行中,如果是添加任务到任务队列
    if (isRunning(c) && workQueue.offer(command)) {
    	// 再次获取ctl校验状态,防止其他线程已经修改ctl值
        int recheck = ctl.get();
        // 校验线程池是否运行中,如果不是运行状态,把任务从工作队列移除
        if (! isRunning(recheck) && remove(command))
        	// 执行拒绝策略
            reject(command);
        // 工作线程状态是 RUNNING && 工作线程数是0,以下两种可能
        // 1. 将核心线程设置为 0 ,所有工作线程都是非核心线程
        // 2. 核心线程全部超时被销毁
        else if (workerCountOf(recheck) == 0)
        	// 添加空任务的非核心线程去处理工作队列中的任务
            addWorker(null, false);
    }
    // 加入到任务队列,如果任务队列满了,执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

addWorker()介绍

/**
  * 根据线程池状态判断是否可以构建一个Worker对象,将Worker对象添加到HashSet中,如果添加成功,启动任务 
  */
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
    	// 重新获取ctl
        int c = ctl.get();
        // 获取运行状态
        int rs = runStateOf(c);
        
        /**
          * 根据线程池状态判断是否要接受新的任务,以及线程池处于SHUTDOWN状态时不再接收新的任务,看看任务
          * 队列是否有任务要进行处理
          * return false有以下几种可能
          * 1. 线程池大于SHUTDOWN
          * 2. 线程池处于SHUTDOWN 并且有了第一个任务
          * 3. 线程池处于SHUTDOWN 没有第一个任务 任务队列为空
          */
        if (rs >= SHUTDOWN && // 线程池处于SHUTDOW STOP TIDYING TERMNATIED状态
            ! (rs == SHUTDOWN && // 是SHUTDOWN状态
               firstTask == null && // 任务为空,代表着补充线程
               ! workQueue.isEmpty())) // 判断工作队列是否为空
            return false;
        
        // 线程池处于运行状态
        for (;;) {
        	// 计算工作线程数量
            int wc = workerCountOf(c);
            /**
              * 校验工作线程数量
              * return false
              * 1. 工作线程大于最大容量
              * 2. 如果是核心线程,工作线程大于核心线程数量;如果是非核心线程,工作线程大于最大线程池的线程数量
              */ 
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) // 根据core判断是否是核心线程
                return false;
            // 工作线程数量加1,如果成功,跳出整个循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // CAS失败重新获取ctl
            c = ctl.get();
            // 判断线程池状态是否是运行时状态
            if (runStateOf(c) != rs)
            	// 说明线程池状态已被其他线程修改,直接结束这一次循环
                continue retry;
            // 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;
            // 上锁,防止其他线程修改workers的数量,保证workers数量同步
            mainLock.lock();
            try {
                // 获取线程池状态
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN || // 线程池处于运行状态
                	// 线程池处于SHUTDOWN且任务队列为空,说明是补充线程
                    (rs == SHUTDOWN && firstTask == null)) { 	
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 将Woreker添加到HashSet
                    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;
}

Worker对象分析

Woreker

thread                     工人进工厂时分配的干活工具
firstTask.              工人进工厂分配的第一个任务
completedTasks     工人进工厂后完成了多少任务
setState(-1)                   代表工人刚进厂,啥都没干

run方法runWorker方法介绍

/**
  * 主要作用就是不断执行getTask方法里的任务
  */
final void runWorker(Worker w) {
	// 获取当前线程
    Thread wt = Thread.currentThread();
    // 拿到第一个任务
    Runnable task = w.firstTask;
    // 将Woreker对象中的第个任务置为空
    w.firstTask = null;
    // 设置线程为可打断状态,之前setState(-1),现在设置为0
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
    	// 第一个任务不为空的话执行完循环的一遍导致task为空,然后从任务队列获取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 如果线程池停止,确保线程被中断;
            // 如果没有,确保线程不被中断。这需要在第二种情况下重新检查,以处理清除中断时的shutdownNow竞争
            if ((runStateAtLeast(ctl.get(), STOP) || // 线程池处于STOP TIDYING TERMIDATED
                 (Thread.interrupted() & // 清除线程池中断状态
                  runStateAtLeast(ctl.get(), STOP)))  // 线程处于STOP TIDYING TERMIDATED
                !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;
                // 将已完成的任务数量加1
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
    	// 执行清理任务
        processWorkerExit(w, completedAbruptly);
    }
}

如何捕捉线程池异常

  1. 重写runWorker方法中afterExecute方法
  2. runWoreker方法中try catch捕捉
  3. Future进行获得。

processWorkerExit方法介绍

private void processWorkerExit(Worker w, boolean completedAbruptly) {
	/**
	  * completedAbruptly为true有两种情跨过
	  * 1. 执行beforeExecute方法发生异常
	  * 2. 执行afterExecute方法发生异常
	  * 说明都是用户发生的异常,为啥getTask发生异常没有导致为true呢,因为getTask方法里面做了try catch处理
	  */ 
    if (completedAbruptly) 
    	// 减少发生用户异常时ctl的工作任务数量
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    	// 将Worker对象中已完成的任务数量加到已完成的数量上
        completedTaskCount += w.completedTasks;
        // 移除Worker
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    tryTerminate();
	// 获取ctl
    int c = ctl.get();
    // 作用是保证线程池里最少有一个线程执行
    if (runStateLessThan(c, STOP)) { // 线程池处于RUNNING或者SHUTDOWN
        if (!completedAbruptly) {
        	/** 
        	  * 判断核心线程是否允许超时,如果允许,核心线程将被销毁
        	  * 如果线程数为0,工作任务队列不为空,那么需要保证最少一个线程执行任务队列的任务
        	  */
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

shutdown方法介绍

检查权限 -> 把线程池状态调整为SHUTDOWN -> 中断空闲线程

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    	// 检查权限
        checkShutdownAccess();
        // 调整线程状态为SHTUDOWN
        advanceRunState(SHUTDOWN);
        // 中断空闲线程
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

interruptIdleWorkers方法介绍

private void interruptIdleWorkers() {
	// false代表是否中断所有线程还是只中断一个线程
    interruptIdleWorkers(false);
}

interruptIdleWorkers方法介绍

private void interruptIdleWorkers(boolean onlyOne) {
     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();
                 }
             }
             // 如果为true,只中断一个线程,不为true中断所有线程
             if (onlyOne)
                 break;
         }
     } finally {
         mainLock.unlock();
     }
 }
如何看线程是否为空闲状态

在线程池execute方法执行会调用Worker类的runWorker方法,这里的lock方法会将线程标志为1lock
acquire
而在interruptIdleWorkers方法中有个tryLock会将标志设置为1,如果不是空闲线程,也就是标志位不能正常转换成功,已经是1的线程,肯定修改失败,就不会执行中断操作。
tryLock
tryAcquire
tryAcquire

shutDownNow方法介绍

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    	// 检查权限
        checkShutdownAccess();
        // 将线程状态调整为stop
        advanceRunState(STOP);
        // 中断所有线程
        interruptWorkers();
        // 获取队列里的任务
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值