ThreadPoolExecutor(JAVA线程池)

ThreadPoolExecutor前提与介绍

  • ThreadPoolExecutor是JAVA线程池的核心实现类之一。

前置知识

  • 了解Runable\Callable
  • 了解Thread
  • 掌握AQS阻塞队列

总体概括

  • java线程池可以对线程进行复用,减少创建和销毁线程的次数,减少资源浪费。
  • java线程池有核心线程数、最大线程数、阻塞队列、饱和策略四个概念。
    • 若线程池线程数小于核心线程数,则新任务都会交由一个新的线程执行,无论已有的线程是否空闲。
    • 若线程池线程数大于等于核心线程,小于最大线程数时,新任务到来会先判断是否有空闲的线程,如果有则先让空闲线程执行,没有则创建新的线程执行。
    • 若线程池线程数等于最大线程数,则新到来的任务会加入阻塞队列。再加入阻塞队列时,若阻塞队列对队列长度有限制,则可能导致加入失败,若失败则根据饱和策略拒绝当前的任务。
    • 饱和策略分为四种:
      • 直接丢弃当前任务。
      • 丢弃当前任务并抛异常。
      • 丢弃之前的任意一个任务并把当前任务排到最前面。
      • 用当前线程执行该任务。

ThreadPoolExecutor核心属性

状态属性及其对应的方法

/**
 * 用于计数线程池中线程的数量和存储当前线程池的状态
 * 高4位用于存储线程池状态
 * 低28位用于存储当前线程池线程数。
 */
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;

/**
 * 最大容量掩码
 * 把掩码与ctl做与操作即可获取到当前线程数。
 */
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 final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

/**
 * CAPACITY是低28都为1的掩码,
 *  c & CAPACITY  可以获取到低位的值,该值表示线程池线程数量
 *
 * @param c 一般是ctl的局部变量
 * @return 获取到当前线程池线程的数量
 */
private static int workerCountOf(int c)  { return c & CAPACITY; }

/**
 * ~CAPACITY是高4位为1的掩码
 *  c & ~CAPACITY  可以获取到高位的值,该值表示线程池的状态
 *
 * @param c 一般是ctl的局部变量
 * @return 获取到当前线程池的状态。
 */
private static int runStateOf(int c)     { return c & ~CAPACITY; }

/**
 * 根据线程池状态和线程数量包装出对应的ctl
 */
private static int ctlOf(int rs, int wc) { return rs | wc; }

/*
   下面三个方法都是比较线程池的状态
   根据线程池状态值的定义可知:
      RUNNING > SHUTDOWN > STOP > TERMINATED
   而且这个顺序是线程池逐渐关闭的状态递增。
   
   @param 下面三个方法的参数c一般都是ctl的局部属性
   @param 下面三个方法的参数s一般都是指定的状态属性。
*/

// 状态不小于指定状态
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;
}

/*
   下面两个方法是通过CAS操作当前线程的数量,并确保原子性。
   这里只是对记录线程数的ctl属性的操作,不是直接操作线程的集合。
*/
// 线程数+1
private boolean compareAndIncrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect + 1);
}
// 线程数-1
private boolean compareAndDecrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect - 1);
}
复制代码

参数属性

/**
 * 阻塞队列
 * 用于存储没有分配到线程执行的任务
 */
private final BlockingQueue<Runnable> workQueue;


/**
 * Worker是对线程的封装类
 * 这里用于存储线程池的所有线程。
 */
private final HashSet<Worker> workers = new HashSet<Worker>();

/**
 * 锁
 */
private final ReentrantLock mainLock = new ReentrantLock();


/**
 * 锁状态
 */
private final Condition termination = mainLock.newCondition();

/**
 * 记录最多线程数
 */
private int largestPoolSize;

/**
 * 完成的任务数
 */
private long completedTaskCount;

/**
 * 线程工厂
 * 用于线程池创建新线程
 */
private volatile ThreadFactory threadFactory;

/**
 * 拒绝策略执行
 * 用于新任务被拒绝执行时调用
 * 默认拒绝策略是AbortPolicy,即抛出异常。
 */
private volatile RejectedExecutionHandler handler;

/**
 * 保留时间
 * 当线程数超过核心线程数是,空闲线程在等待一定时间后都没有获取到新任务则会被释放
 * 该字段就是描述空闲线程等待的时间。
 */
private volatile long keepAliveTime;

/**
 * 核心线程是否释放
 * 若为true,核心线程等待keepAliveTime后则被释放。
 * 若为false,则核心线程空闲也不会被释放
 */
private volatile boolean allowCoreThreadTimeOut;

/**
 * 核心线程数
 */
private volatile int corePoolSize;

/**
 * 最大线程数
 */
private volatile int maximumPoolSize;
复制代码

ThreadPoolExecutor内部类讲解

  • Worker: 工作线程,任务执行的基本单位,可以复用线程。
  • 拒绝策略:ThreadPoolExecutor的拒绝策略接口及4个自身实现的拒绝策略。

Worker

/**
 * Worker是ThreadPoolExecutor最基本的执行单位。
 * Worker实现了Runable接口,可以作为独立线程运行。
 *
 * Worker继承了AQS的锁阻塞机制,实现了获取锁的方法
 * worker在执行任务的run方法时,会锁定worker本身,
 * 而在从阻塞队列获取任务的过程中则没有上锁。
 * 通常可以通过尝试获取worker的锁来判断worker是否空闲
 * 在SHUTDOWN下,worker通常在空闲情况下才会被打断。
 * 若在STOP状态,则worker即使在执行任务也会被打断。
 */
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
{
    private static final long serialVersionUID = 6138294804551838833L;

    /**
     * 当前worker管理的线程
     */
    final Thread thread;
    
    /**
     * 用于初始化worker时执行的第一个任务
     */
    Runnable firstTask;
    
    /**
     * 用于计算当前worker执行的任务数
     */
    volatile long completedTasks;

    /**
     * 构造器
     */
    Worker(Runnable firstTask) {
        setState(-1); // 只要state不是0即上锁
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /**
     * 启动方法
     */
    public void run() {
        // 调用了ThreadPoolExecutor的方法
        // 这里只是作为一个代理。
        // 方法在下面展示
        runWorker(this);
    }

    // 实现了AQS获取锁的方法
    protected boolean tryAcquire(int unused) {
        // 使用CAS把锁状态更换成1
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    // 实现了AQS释放锁的方法
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0); // state为0则为没上锁
        return true;
    }

    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
    
    // 如果当前worker已在运行中,则中断它。
    void interruptIfStarted() {
        Thread t;
        // 当前工作线程已上锁(正在执行任务逻辑)
        // 且线程不为空
        // 且没有被中断
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt(); // 强行中断
            } catch (SecurityException ignore) {
            }
        }
    }
}
复制代码

拒绝策略

拒绝策略用于任务不被线程池接收时做出的对应处理。

  • RejectedExecutionHandler 拒绝策略接口定义
  • AbortPolicy 直接抛出异常
  • CallerRunsPolicy 当前执行该策略的线程执行该任务
  • DiscardPolicy 直接丢弃任务,不做任何处理
  • DiscardOldestPolicy 抛弃最近即将执行的任务,并重新尝试把当前任务加入任务列表。

RejectedExecutionHandler接口

/**
 * 该接口用于定义无法被线程池接收时对任务的处理策略。
 * 当线程数超过了最大线程数,或者线程池已关闭却接收到新任务时被触发。
 */
public interface RejectedExecutionHandler {
    /**
     * 任务被拒绝的回调方法。
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
复制代码

AbortPolicy

/**
 * ThreadPoolExecutor提供的拒绝策略之一,默认的拒绝策略
 * 直接抛出RejectedExecutionException方法。
 */
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}
复制代码

CallerRunsPolicy

/**
 * ThreadPoolExecutor提供的拒绝策略之一
 * 直接使用当前线程执行被拒绝任务
 */
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}
复制代码

DiscardPolicy

/**
 * ThreadPoolExecutor提供的拒绝策略之一
 * 直接丢弃,不做任何处理
 */
public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}
复制代码

DiscardOldestPolicy

/**
 * ThreadPoolExecutor提供的拒绝策略之一
 * 从阻塞队列中取出一个即将执行的任务丢弃,重新尝试执行该任务。
 */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}
复制代码

ThreadPoolExecutor核心逻辑追踪

执行任务逻辑

  • execute方法:执行无返回值任务
    • addWorker方法:添加新的工作线程
    • reject方法:拒绝任务策略
  • submit方法:执行任务,且有返回值。

execute方法

/**
 * execute是线程池执行线程的方法。
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 如果当前线程数量小于核心线程数
    if (workerCountOf(c) < corePoolSize) {
        // 直接创建一个新线程
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 运行到这表示线程池线程数大于等于核心线程数
    // 判断线程池是否在运行中,且是否成功把任务添加到队列中
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 如果线程池没在运行则拒绝该任务
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程池仍在执行,而线程数为0
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false); // 尝试新建一个线程
    }
    // 尝试添加一个新线程
    else if (!addWorker(command, false))
        reject(command); // 执行拒绝策略。
}
复制代码

addWorker方法

/**
 * 尝试添加新的线程
 * execute通过调用该方法添加新的线程
 * @return 若添加成功则返回true,否则返回false
 */
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;

        // 通过CAS增加线程数
        for (;;) {
            int wc = workerCountOf(c); // 获取线程数
            // 判断是否超过线程限制
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 通过CAS添加线程数,成功则跳出循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  
            // 判断ctl是否被更改
            if (runStateOf(c) != rs)
                continue retry;
        }
    }

    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 {
                int rs = runStateOf(ctl.get());
                
                // 判断线程池是否正在运行
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    // 这里表示线程池还未激活线程,线程却已经被激活
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s; // 记录最大线程数
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start(); // 启动线程,执行worker的run方法
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            // 如果线程加入失败,则调用加入失败方法。
            addWorkerFailed(w);
    }
    return workerStarted;
}
复制代码

reject方法

/**
 * 执行拒绝策略
 * execute在尝试把添加任务失败时,会执行拒绝策略方法。
 * 
 */
final void reject(Runnable command) {
    // private volatile RejectedExecutionHandler handler;
    // 默认是AbortPolicy,即直接抛异常。
    handler.rejectedExecution(command, this);
}
复制代码

submit方法

/*
 * submit方法是执行有返回值的任务。
 * 下面三个方法都是在AbstractExecutorService中实现。
 * ThreadPoolExecutor是AbstractExecutorService实现类,
 * 且继续使用了AbstractExecutorService的逻辑。
 *
 * 三个方法都是把传入的参数封装成RunnableFuture,
 * 再调用execute方法执行。
 */
 
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}
复制代码

复用线程逻辑

前面回顾

  1. 开发者调用了ThreadPoolExecutor的execute方法,传入Runnable执行的内容。
  2. ThreadPoolExecutor调用了addWorker方法尝试添加任务进线程池。
  3. addWorker方法在创建了worker执行任务后,在最后会通过调用worker的start方法激活worker。
  4. worker的run方法只调用了ThreadPoolExecutor的runWorker方法。

runWorker方法

/**
 * worker的执行逻辑
 * 该方法不断从阻塞队列中获取任务执行。
 * 若获取不到任务,则根据配置判断是否要释放线程。
 */
final void runWorker(Worker w) {
    // 获取worker当前的线程
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    
    // ThreadPoolExecutor中若需要中断当前worker线程
    // 需要提前获取worker的锁。
    // 此处worker解锁可以确保该线程能够被中断。
    // worker重写了释放锁方法,即使没有锁状态释放锁也不会报错,
    // 且可以把锁置为解锁的状态。
    w.unlock();
    boolean completedAbruptly = true;
    try {
        // 每次循环通过getTask方法获取新的任务。
        // 当获取的任务为空时,当前worker则会结束并被销毁。
        while (task != null || (task = getTask()) != null) {
            w.lock(); // 上锁
            // 如果线程池是STOP、TIDYING、TERMINATED状态
            // 而线程没有被中断,则中断当前线程
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            
            try {
                // 这是一个保留方法
                // 可以在任务开始执行之前做初始化操作
                // ThreadPoolExecutor在这里是空方法
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run(); // 调用任务的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 {
                    // 这是也是一个保留方法,跟beforeExecute相似
                    // 可以在任务开始执行之后做初始化操作
                    // ThreadPoolExecutor在这里也是空方法
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++; // 统计执行过的任务数
                w.unlock(); // 解锁
            }
        }
        completedAbruptly = false;
    } finally {
        // worker被销毁的清理方法。
        // 这里包括把当前worker从线程集合移除,线程数减一等操作。
        processWorkerExit(w, completedAbruptly);
    }
}
复制代码

getTask方法

/**
 * 从阻塞队列中获取任务
 * 若返回null,则worker会被释放销毁
 */
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;
        }
        
        int wc = workerCountOf(c); // 获取当前线程数

        // 当前线程是否会超时
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 当前线程数是否超出了最大线程数量
        // 或者当前线程已超时
        // 满足上面两个条件之一,且线程数大于1或者阻塞队列为空,
        // 则尝试减少一个worker计数并返回任务为空。
        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;
        }
    }
}
复制代码

processWorkerExit方法

/**
 * 当一个worker被销毁时执行
 *
 * worker被销毁有两种情况,
 * 一种是getTask时获取到的任务是null
 * 另一种是run的时候出现异常被捕捉,worker也会被销毁
 *
 * @param w 表示要被销毁的worker
 * @param completedAbruptly 表示是否因为异常而销毁
 */
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    if (completedAbruptly) 
        // 若因为异常而执行该方法
        // ctl中的线程计数则还没有被调整
        // 这里用CAS+自旋调整ctl值。
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock(); // 上锁
    try {
        // 把当前worker的任务计数累加到线程池中。
        completedTaskCount += w.completedTasks;
        workers.remove(w); // 线程集合移除该worker
    } finally {
        mainLock.unlock();
    }

    // 当线程数减少时都会调用该方法
    // 判断线程池是否要关闭,若是则帮忙中断空闲线程。
    tryTerminate();

    int c = ctl.get();
    // 如果线程池正在运行
    if (runStateLessThan(c, STOP)) {
        // 如果是获取不到新任务而被销毁
        if (!completedAbruptly) {
            // 这里计算需要的worker的最小值
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 如果当前工作线程数大于最小线程数,则返回
            if (workerCountOf(c) >= min)
                return; 
        }
        // 如果当前工作线程数小于最小线程数,则创建一个新工作线程。
        addWorker(null, false);
    }
}
复制代码

关闭线程池逻辑

  • shutdown方法:关闭线程池
    • tryTerminate方法:尝试终止线程池
    • interruptIdleWorkers方法:遍历并终止空闲线程
  • shutdownNow方法:立刻关闭线程池
    • interruptWorkers方法:终止活跃的工作线程

shutdown方法

/**
 * ThreadPoolExecutor外部通过调用该方法关闭线程池
 * 该方法被执行后,线程池状态被设为SHUTDOWN,
 * 该状态下线程池不再接收新的任务,但会继续执行队列中的任务,
 * 直到全部执行完毕。
 */
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();  // 确定有关闭线程的权限
        advanceRunState(SHUTDOWN); // 设为关闭状态
        interruptIdleWorkers(); // 中断所有空闲工作线程
        onShutdown(); // 空方法,用于被重写。
    } finally {
        mainLock.unlock();
    }
    // 尝试终止线程池
    tryTerminate();
}
复制代码

tryTerminate方法

/**
 * 该方法尝试帮忙从SHUTDOWN状态过渡到TERMINATED状态。
 */
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 如果线程池还在运行状态
        // 或者已经处于整理状态或者终止状态
        // 或者线程池处于关闭状态但任务队列还未为空
        // 则直接返回,不做处理操作
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
            
        // 若不符合上面情况,且工作线程数不为0
        // 则尝试中止一个工作线程
        if (workerCountOf(c) != 0) {
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        // 来到这里表示当前处于SHUTDOWN或STOP态,
        // 且工作线程为0
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 用CAS把线程池状态设为整理态。
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    // 用于终止线程池时执行的额外操作。
                    // 这里是一个空方法
                    terminated();
                } finally {
                    // 把线程池设为终止态。
                    ctl.set(ctlOf(TERMINATED, 0));
                    // 唤醒其他等待mainLock的线程
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
    }
}
复制代码

interruptIdleWorkers方法

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

/**
 * 终止空闲的工作线程
 *
 * @param onlyOne 是否只中断一个工作线程
 */
private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 遍历线程
        for (Worker w : workers) {
            Thread t = w.thread;
            // 判断t是否被中断且处于空闲
            // 若能获取到锁表示w当前空闲。
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    // 若空闲则中断它
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            // 若标志了onlyOne,
            // 则无论第一个工作线程是否被中断
            // 都返回。
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}
复制代码

shutdownNow方法

/**
 * 立刻关闭线程池
 * 把线程池状态设为STOP
 * 所有工作线程无论是否活跃都会被中断。
 */
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP); // 设为STOP状态
        interruptWorkers(); // 中断活跃状态下工作线程
        tasks = drainQueue(); // 获取剩余未执行的任务
    } finally {
        mainLock.unlock();
    }
    // 终止线程池
    // STOP状态下会尝试终止空闲线程
    // 若所有工作线程都为空,则把线程池转化为终止态。
    tryTerminate(); 
    return tasks; // 返回没有执行的任务列表
}
复制代码

interruptWorkers方法

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            // 调用worker自身实现的中断方法
            // 这里只中断了活跃状态下的工作线程
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}
复制代码

Executors工厂

Executors 是java为ThreadPoolExecutor提供的工厂类。

  • 固定线程池:最大线程数和核心线程数一致。

  • 缓存线程池:核心线程数为0,最大线程数是Integer的最大值

固定线程池

/**
 * 指定线程数,最大线程与核心线程数一致。
 * 若核心线程不被保留,则一旦获取不到任务就会被释放(默认是保留)
 * 使用LinkedBlockingQueue作为队列,该队列长度为int的最大值。
 */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

/**
 * 该方法与上面类似,唯一区别是指定了线程工厂。
 */
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}
复制代码

缓存线程池

/**
 * 核心线程数为0,最大线程数是无限大
 * 线程保留60秒,或60秒内没有新任务才销毁
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

/**
 * 该方法与上面类似,唯一区别是指定了线程工厂。
 */
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}
复制代码

总结

线程池状态总结

  • 一开始创建及其运行时都处于运行态RUNNING。
    • 运行态可以转变为关闭态或者停止态。
  • 若调用shutdown方法,则进入关闭态。
    • 关闭态不再接收任务,但会继续处理剩余的任务。
    • 若所有任务都被执行,任务列表为空,则会逐渐把没有获取到的任务的工作线程停止销毁。
    • 关闭态下一个状态通常是整理态。
  • 若调用了shutDownNow方法,则会进入停止态STOP。
    • 该状态下会中断所有线程,从而尽快过渡到下一个装态。
    • 停止态下一个状态是整理态。
  • 若所有工作线程都停止,则进入到在整理态TIDYING。
    • 该状态下会执行termined方法,用于回收清理操作。ThreadPoolExecutor的该方法是空方法,仅保留为子类重写使用。
    • 整理态下一个状态是终止态。
  • 若整理态执行完对应的清理工作,则进入终止态TERMINED,线程池彻底关闭。

worker的锁及其工作流程

  • worker是ThreadPoolExecutor中最基本的线程执行单位,且拥有复用线程的能力。

  • Worker因为继承并实现AQS而拥有了锁的方法。
  • 为了避免worker在执行任务过程中被中断,在执行任务前后都会加锁解锁。worker被加锁时处于活跃态,没有锁时则处于空闲态。
  • worker的锁某种程度上像一个带保护措施标记,该锁的解锁可以在没加锁的情况下正常执行。

工作流程

  • 活跃态:执行任务的run方法。此时持有锁,且一旦有异常被抛出,则终止该线程,执行该worker销毁操作。若顺利执行完毕,则解锁进入空闲态。
  • 空闲态:获取任务。该状态下没有获取锁,可能会被中断。空闲状态主要是去获取任务,获取时会判断线程池状态,当前线程状态(是否被中断),是否有未完成任务,是否要释放当前worker等。若能获取到的任务为空,则表示获取任务失败,当前的worker会被销毁。

线程池机制

核心线程默认会一直保留,最大线程在一定时间内没有领取到任务会被销毁,超出最大线程的任务会被阻塞队列缓存,若阻塞队列放不下则会采取拒绝策略。

猜你喜欢

转载自juejin.im/post/5ead442cf265da7b925cef26