线程池

目标

1、线程池中线程的运行状态
2、worker的计算
3、线程池中ctr的作用
4、任务添加流程
5、任务处理的逻辑

总结

先写出流程结论, 后续再分析时通过代码对这些结论进行验证;
  1、添加Task时, 首先判断WorkerCount与corePoolSize的关系, 如果WorkerCount < corePoolSize, 此时会无条件创建一个Worker来执行Task, 同时将ctl的低位执行+1操作, 此时在创建Worker时, 会将Task与Worker进行关联, Worker执行完Task以后将Task进行释放;
  2、如果WorkerCount ≥ corePoolSize, 此时首先会将Task添加到任务队列中, 添加完成之后, 会再次判断WorkerCount, 此时只有当WorkerCount == 0时才会再次创建新的WorkerCount来执行任务, 但是此时通过addWorker创建Worker时, 没有将Worker与Task进行关联. 到这里有三个疑问: 1. WorkerCount ≥ corePoolSize与WorkerCount==0有没有冲突? 2. 如果后者WorkerCount > 0, 并不会调用addWorker创建新的线程, 那么此时提交的Task被谁执行? 3. 如果后者WorkerCount == 0, 那么addWorker创建的Worker又是如何执行Task的呢?
  3、如果WorkerCount ≥ corePoolSize && workQueue.offer(task) == false, 也就是说第二个条件任务队列已满的情况下, 此时会触发addWorker尝试为当前Task创建新的Worker, 此时为何说是尝试? 因为如果线程池中线程数量已经大于线程池运行的最大线程数时, 此时将不会再为新的Task创建Worker, 而是采取拒绝该Task的方式
  4、接下来是任务处理的流程, 同时也回答了第二步中的几个疑问, 当线程执行完Task之后, 尝试从任务队列中获取Task, 针对线程获取Task分两种情况: (1) 核心线程获取Task, 如果不允许核心线程超时的情况下, 如果workQueue.isNotEmpty, 此时核心线程将一直处于挂起状态, 直到任务队列中添加了元素. (2) 针对普通线程, 如果当前workQueue.isEmpty, 那么普通线程会挂起指定时间后进行释放操作, 同时执行ctr--操作.
  5、所以第二条的疑问也得到的解释, 任务被添加到任务队列之前, 线程池中的线程刚好全部被释放, 此时会重新创建线程执行workQueue中的Task. 任务被添加到任务队列之后, 如果WorkerCount != 0, 此时会直接唤醒等待挂起的线程, 避免线程的不必要创建.

一、关于Executor

1.1 线程池的顶层接口Executor
public interface Executor {
    void execute(Runnable command);
}
1.2 线程池的第二层接口ExecutorService
public interface ExecutorService extends Executor {
    void shutdown();
    List shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
     Future submit(Callable task);
     Future submit(Runnable task, T result);
    Future submit(Runnable task);
}

1、我们经常用到的几个线程池都是实现于ExecutorService接口, ExecutorService对Executor进行了扩展, 支持Callable和Runnable;
2、先不考虑ExecutorService每个方法到底什么作用, 待后边分析到线程池管理线程流程时自然明了;

1.3 线程池的提供者Executors:
public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads) {...}
    public static ExecutorService newWorkStealingPool() {...}
    public static ExecutorService newSingleThreadExecutor() {...}
    public static ExecutorService newCachedThreadPool() {...}
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {...}
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {...}
    private Executors() {}
}

  1、先忽略这些方法的内部实现, 以及传入的参数, 现在分析还为时过早, 属于强行记忆;
  2、这些方法主要返回两大类线程池ExecutorService和ScheduledExecutorService, 而ScheduledExecutorService又是继承于ExecutorService, 所以后续会重点关注这两个类;
  3、接下来开始分析ScheduledExecutorService和ExecutorService是如何管理内部维护的线程池;
  4、对Executors 方法展开, 发现ExecutorService和ScheduledExecutorService分别指向ThreadPoolExecutor和ScheduledThreadPoolExecutor, 而这两个类又都间接实现ExecutorService接口, 所以都支持execute(RunnableImpl)和submit(CallableImpl)方式;
先分析execute(RunnableImple)这种方式;
  5、关于四个方法的具体分析在模块三

在进行分析之前, 先列出线程池中线程的几个状态以及构建线程池时所需要的参数, 后续分析源码时遇到即回头来进行补充说明:
线程的几个状态值 :

private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

构建线程池时所需参数 :

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
        ...
}

下面结合对ThreadPoolExecutor源码的分析, 对每个参数的作用进行总结描述:

其他参数 含义
corePoolSize 注意没有任务时, 核心线程的状态, 与allowCoreThreadTimeOut有关
maximumPoolSize 当有任务到来时, 通过该变量判断当前任务应该如何处理
keepAliveTime 如果元素队列为空时, 线程(包括核心线程和非核心线程)被挂起的时间
TimeUnit 如果元素队列为空时, 线程(包括核心线程和非核心线程)被挂起的时间单位
BlockingQueue 元素出入队列的方式
ThreadFactory 自定义线程创建的方式
RejectedExecutionHandler 任务被提交到线程池时, 如果当前线程池中线程数量大于最大工作线程数量时, 该任务会被拒绝, 拒绝策略取决于构造线程池时传入的具体的拒绝策略

二、分析线程池里面涉及到的位运算

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 默认情况下ctl = 11100000 00000000 00000000 00000000;
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // CAPACITY = 0001111 11111111 11111111 11111111
    // ~CAPACITY = 11100000 00000000 00000000 00000000
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    // runState is stored in the high-order bits
    // 将线程池的运行状态记录在bits的高位
    // RUNNING = 11100000 00000000 00000000 00000000
    private static final int RUNNING    = -1 << COUNT_BITS;
    // SHUTDOWN = 00000000 00000000 00000000 00000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // STOP = 00100000 00000000 00000000 00000000
    private static final int STOP       =  1 << COUNT_BITS;
    // TIDYING = 01000000 00000000 00000000 00000000
    private static final int TIDYING    =  2 << COUNT_BITS;
    // TERMINATED = 01100000 00000000 00000000 00000000
    private static final int TERMINATED =  3 << COUNT_BITS;
    // 通过计算获取高位三位的值, 进而判断当前线程池的运行状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // CAPACITY的高三位是0, 所以线程池中Worker的数量存储在低位29位中.
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    //
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    /*
     * Bit field accessors that don't require unpacking ctl.
     * These depend on the bit layout and on workerCount being never negative.
     */
    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }
    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }
}

上面的运算结合下面实际操作进行分析.

三、任务提交

3.1 ThreadPoolExecutor.execute

注意Thread与Worker的关系

虽然代码少, 但是要表达的意思却是很多的;
public void execute(Runnable command) {
    // c默认 = RUNNING, 二进制 = 11100000 00000000 00000000 00000000
    int c = ctl.get();
    // 1. 通过workerCountOf内部与CAPACITY进行与运行得到当前线程池中Worker的数量;
    // 2. 然后将当前WorkerCount与corePoolSize进行比较, 如果WorkerCount < corePoolSize, 
    //    则进入if内部通过addWorker尝试为当前Runnable创建线程.
    // 3. 如果当前WorkerCount ≥ corePoolSize
    if (workerCountOf(c) < corePoolSize) {
        // 1. 注意此时传参command(command != null), true;
        // 2. 针对返回的结果, 有以下几种情况:
        //    true:   结合addWorker源码可知, 如果线程池处于运行状态, 返回true;
        //    false:  只有线程池处于非运行状态, 才可能返回false, 这种情况不考虑;
        if (addWorker(command, true))  
            return;
        // 再次获取当前线程池的一个状态
        c = ctl.get();
    }
    // 1. 因为我们一直假设线程池处于运行状态, 所以此时默认isRunning(c)=true, 直接分析第二个条件;
    // 2. 针对第二个条件, workQueue.offer:
    //    (1) true: 任务队列未满;
    //    (2) false: 任务队列已满;  
    // 3. 然后如果当WorkerCount ≥ corePoolSize时, 首先将任务进行入队操作.
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 只考虑线程池处于RUNNGING的场景, 跳过该if语句;
        if (!isRunning(recheck) && remove(command))
            reject(command);
        // 再次获取线程池中Worker的数量, 此时只有当WorkerCount == 0时, 才会进入到if语句中
        // 调用addWorker创建新的Worker实例, 而结合上下文可知, Worker与Thread是一个1:1对应
        // 的关系, 所以如果WorkerCount == 0, 也就是说当前线程池中线程数量为0;
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);     
    }
    // 1. 能执行到这里需要满足以下几个条件:
    //    (1) wc > corePoolSize即当前线程池中工作线程数量>核心线程数量;
    //    (2) workQueue.offer(command) = false, 即任务队列已满;
    // 2. 结合addWorker源码分析可知, 如果任务被成功执行, 返回true, 如果WorkerCount ≥ maximumPoolSize, 
    //    返回false, 此时对该任务才去拒绝策略;
    else if (!addWorker(command, false))
        reject(command);    
    }
}

关于传值的问题, 这里用一张表进行总结: 接下表中数据对execute代码进行分析

线程池_第1张图片

对上述表中的以及代码中的注释再次解释一下:
1、添加任务时, 如果WorkerCount < corePoolSize, 那么此时会直接创建一个线程来执行该任务;
2、添加任务时, 如果WorkerCount ≥ corePoolSize, 那么此时会先将当前线程进行入队操作, 然后再次判断WorkerCount, 只有当WorkerCount == 0 时, 才会触发addWorker创建Thread去执行任务队列中的任务. 现在思考一个问题, 如果WorkerCount != 0, 提交的任务被谁处理了呢?
3、如果WorkerCount ≥ corePoolSize并且任务队列已满, 此时会尝试调用addWorker创建新的线程来执行提交的任务.

3.2 ThreadPoolExecutor.addWorker
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        // 再次获取当前线程池的状态: 高位存储线程池的运行状态.
        int c = ctl.get();
        // 通过高位获取当前线程池的运行状态;
        int rs = runStateOf(c);
        // 不考虑线程池中断的情况, 所以继续向下执行;
        if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
            return false;
        for (;;) {
            // 通过低位29位的值获取当前线程池中WorkerCount;
            int wc = workerCountOf(c);
            // 忽略第一个条件, 直接看第二个条件, 第二个条件重点在于core;
            // 而core的传值又依赖于execute里面的几种情况:
            // 1. core = true:
            //    当wc < corePoolSize, 触发addWorker会传入core = true, 所以此时第二个条件为false;
            // 2. core = false:
            //    (1) wc ≥ corePoolSize && workQueue.offer == true && WorkerCount == 0
            //    (2) wc ≥ corePoolSize && workQueue.offer == false
            // 3. 不管core为true还是false, 只要线程池正常运行, 第二个条件就是始终为false;
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 只要程序能够正常运行, 都会在这里执行ctl+1操作, 而ctl低29位标志的是线程池中线程数量
            // 对ctl执行+1操作
            if (compareAndIncrementWorkerCount(c))
                break retry;
        }
    }
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 关于firstTask取值情况上文已经用表进行了说明, 此处再简短的进行说明:
        // 1. firstTask != null:
        //    (1) wc < corePoolSize;
        //    (2) wc ≥ corePoolSize && workQueue.offer() == flase;
        // 2. firstTask == null:
        //    wc ≥ corePoolSize && workQueue.offer() == true && workerCount == 0;
        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());
                // 假设线程池是运行状态, 因此第一个条件为true;
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    // 标志位, 通过该变量判断是否返回true/false;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 触发其内部Thread.run的执行;
                t.start();    
                workerStarted = true;
            }
        }
    } finally {
        if (!workerStarted)
            // 如果执行失败, 将ctl进行-1操作;
            addWorkerFailed(w);
    }
    return workerStarted;
}

对addWorker进行总结:
  1、线程池处于正常运行状态, 只要触发了addWorker方法, 不论当前线程池中线程数量多少, 都会重新创建一个新的Worker与Thread, 然后通过该Worker与Thread来执行新的任务;
  2、有一个问题是当wc ≥ corePoolSize时, firstTask需要先被添加到workQueue中, 然后再次判断只有当wc == 0时, 才会触发addWorker创建新的Worker进行任务的执行, 那么问题来了, 此时调用addWorker传入的firstTask = null, 那么线程从哪里获取的任务进行执行呢?
  3、接着第二个条件如果wc != 0, 此时是不会触发addWorker的, 那么此时提交的任务又是被谁执行呢?
上面第二个和第三个问题在Worker.run中会得到答案

3.3 ThreadPoolExecutor.compareAndIncrementWorkerCount执行ctl+1操作
private boolean compareAndIncrementWorkerCount(int expect) {
    //ctl执行+1操作, 执行完这个操作之后, 开始创建Worker实例, 然后结合上下文
    //可知, ctl高三位记录的是当前线程池的状态, 低29位记录的是当前线程池中Worker数量.
    return ctl.compareAndSet(expect, expect + 1);
}
3.4 Worker结构
// Worker与Thread、Runnable是1:1:1对应的一个关系, 但是firstTask可能存在为null的情况.
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    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);
    }
}

四、关于Worker

4.1 Worker.run执行任务
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    public void run() {
        // 触发线程池中的runWorker方法;
        runWorker(this);
    }
}

public class ThreadPoolExecutor {
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // 问题就在这里, 如果task == null的情况下, 当前线程通过getTask()会尝试从
            // 任务队列中获取任务进行执行;
            while (task != null || (task = getTask()) != null) {
                w.lock();
                task.run();
                task = null;
                w.completedTasks++;
                w.unlock();
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
}
4.2 ThreadPoolExecutor.getTask线程从任务队列中获取任务
先写结论:
1. 当允许核心线程超时: 从元素队列中取出元素时, 如果元素队列为空, 当前线程会先挂起指定时间, 然后被结束;
2. 当不允许核心线程超时: 从元素队列中取出元素时, 如果当前元素队列为空, 当前线程为核心线程时, 线程被挂起,
   如果当前线程为非核心工作线程, 当前线程会挂起指定时间然后被结束;
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 目前只考虑线程池正常运行的情况, 所以不考虑这段流程;
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        // 再次获取WorkerCount;
        int wc = workerCountOf(c);
        // 对time取值有以下几种情况:
        // 1. timed = true:
        //    1.1 allowCoreThreadTimeOut = true: 通过allowCoreThreadTimeOut()进行赋值;
        //    1.2 wc > corePoolSize: 当前线程数量>核心工作线程数量;
        // 2. timed = false:
        //    2.1 wc ≤ corePoolSize: 当前线程数量 ≤ 核心工作线程数量;
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        // 第一个条件wc > maximumPoolSize认为是true, 所以重点在于后面两个条件:
        // (1) timed = true && timedOut == true
        //    针对第一个条件timed=true, 也就是说允许核心线程超时或者当前线程数量超过核心线程数量;
        //    第二个条件timedOut作为超时的标志, 结合下文如果r == null, 则认为当前线程超时成功;
        // (2) wc > 1 || workQueue.isEmpty()
        //    在当前线程超时成功的情况下, 如果任务队列此时还是为空的, 那么将会释放掉该线程, 通过
        //    进入if内执行ctl--操作
        // 通过对这段代码进行分析还可以发现一个结论, 对于超时的线程, 再进行释放之前还会再去尝试一下
        // 从任务队列中获取任务, 如果此时任务队列中还是没有任务, 然后释放该线程.
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        // 1. timed = true:
        //    此时allowCoreThreadTimeOut = true || wc > corePoolSize;
        // 2. timed = false: 
        //    结合上文可知, 此时allowCoreThreadTimeOut = false && wc ≤ corePoolSize;
        // 3. BlockingQueue的特性就是pool(...)当前线程在等待指定时间后进行元素出队操作;
        // 4. take()方法如果元素队列为空, 当前线程是会一直处于挂起状态;
        Runnable r = timed ?
            workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
        if (r != null)
            return r;
        // 执行到这里需要满足的条件是r == null, 然后通过timedOut来认为获取任务失败是一种超时的情况.
        timedOut = true;
    }
}

五、Executors四个常见线程池

5.1 Executors.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}
5.2 Executors.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
}
5.3 Executors.newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
}
5.4 Executors.newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public class ScheduledThreadPoolExecutor  extends ThreadPoolExecutor implements ScheduledExecutorService {
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
    }
}
public class ThreadPoolExecutor extends AbstractExecutorService {
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
}
线程池_第2张图片

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