【JUC】Executors常用线程池

732 阅读8分钟

一、线程池

线程池的好处:

  1. 减少了创建和销毁的次数,每个工作线程都可以重复利用;
  2. 可以根据系统的承受能力设置线程池中的线程数量,防止由于创建过多的线程导致内存占用过大最后死机。

二、Executors提供的常用线程池

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:当线程数大于核心时,空闲线程存活时间
  • unit:存活时间单位
  • workQueue:保持任务的队列
  • threadFactory:执行程序创建线程的工厂
  • handler:超出队列而使用的拒绝策略

1、newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务;此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

corePoolSize、maximumPoolSize都为1的线程池,无界队列;此线程池一般用于顺序执行任务。

2、newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

corePoolSize、maximumPoolSize一样,线程数不会超过nThreads,也不会出现空闲线程回收

3、newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全取决于系统能够创建的最大线程大小。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

无核心线程数,最大线程数为int的最大值,使用SynchronousQueue队列,此队列不存放任务,所以每次有任务过来如果没有空闲线程就会创建新的线程来执行此任务

4、newScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
  super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
        new DelayedWorkQueue());
}

与其他线程池不一样的是,这里使用的DelayedWorkQueue延迟队列

三、拒绝策略

CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

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

AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException(默认的拒绝策略)

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

DiscardPolicy:任务将被删除(此拒绝策略什么都不做,将会抛弃此任务)

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

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

四、源码分析

首先我们看看成员变量,线程池的设计很巧妙,使用高3位保存线程池运行状态,低29位保存线程数量

// 线程数和运行状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29,用于位移
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池最大支持线程的容量(0001 1111 1111 1111 1111 1111 1111 1111)
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// 线程池运行状态 (1110 0000 0000 0000 0000 0000 0000 0000)
private static final int RUNNING    = -1 << COUNT_BITS;
// 0000
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 0010 0000 0000 0000 0000 0000 0000 0000
private static final int STOP       =  1 << COUNT_BITS;
// 0100
private static final int TIDYING    =  2 << COUNT_BITS;
// 0110
private static final int TERMINATED =  3 << COUNT_BITS;

在看看经常用到获取数量和状态的几个工具方法

// c&高三位为1 低29位为0;也就是使高三位不变 低29为0  运行状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 使高三位为0低29为不变  工作线程的数量
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 把状态和线程数量整合
private static int ctlOf(int rs, int wc) { return rs | wc; }

execute()

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            // 工作线程数小于corePoolSize,加入工作线程,运行
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            // 线程池running,并且成功加入队列
            int recheck = ctl.get();
            // 重新校验是否running,否则移出队列,拒绝
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                // 没有工作线程,开启新线程,来消费加入队列中的command
                addWorker(null, false);
        }
        // 无法加入队列也无法加入工作线程,拒绝
        else if (!addWorker(command, false))
            reject(command);
    }

以上代码主要是三个步骤

  • 1、运行线程数小于corePoolSize,尝试创建线程把command作为第一个task执行
  • 2、如果能成功加入队列,再次确认,可能在添加时线程已经死掉,回滚队列;如果没有线程则创建线程
  • 3、如果无法加入队列,则尝试创建新的线程,如果失败则拒绝

addWorker()

1、判断线程池状态能否添加 2、判断是否超出线程数 3、生成worker,加锁再次校验线程池状态 4、运行线程 5、若校验失败或者线程开启失败则回滚

private boolean addWorker(Runnable firstTask, boolean core) {
        // 用于跳出循环
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 如果线程池不是running并且不是SHUTDOWN或者firstTask不为null或者队列为空直接返回false
            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;
                if (compareAndIncrementWorkerCount(c))
                    break retry; 
                c = ctl.get();  // Re-read ctl
                // 线程池状态变化,跳出循环重新执行
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        //  到了这里就表示线程数量已经成功添加
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 创建工作线程
            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());
                    // running或者(SHUTDOWN且firstTask=null,说明队列有任务)
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // 线程已经开始运行
                            throw new IllegalThreadStateException();
                        // 使用set保存任务
                        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); // 失败回滚addWorker
        }
        return workerStarted;
    }

addWorkerFailed()

线程添加失败回滚操作:

private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);// 移除工作set
            decrementWorkerCount();// 工作线程数量减一
            tryTerminate();// 尝试终止线程池
        } finally {
            mainLock.unlock();
        }
    }

五、Worker工作线程的实现

private final class Worker extends AbstractQueuedSynchronizer implements Runnable Work实现了Runable接口和AQS

成员变量

// worker运行的线程
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);
}

执行任务的方法

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

使用getTask获取任务;然后执行;如果获取为null;则进入processWorkerExit退出线程,如果小于核心线程数创建一个新的worker

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()去获取task
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 如果线程池是stop,则中断;否则清除中断
            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 {
        // 意外结束,移除当前worker,如果未超出核心线程数,则重新创建一个新的worker
        processWorkerExit(w, completedAbruptly);
    }
}

getTask

如果需要回收当前线程则返回null。否则一直阻塞或者返回任务

private Runnable getTask() {
        // 是否超时
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check worker是否不再处理任务 stop则直接返回null;shutdown则需要队列为空
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // 是否可回收   核心线程超时回收;大于核心线程数
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 可回收并且存在可回收线程
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                // 根据标识判断使用take还是poll
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

六、线程池关闭

shutdown

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 自旋+CAS修改状态(之后将无法加入任务,处理完队列中的任务后,将会被终止)
            advanceRunState(SHUTDOWN);
            // 中断所有工作线程
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 尝试终止线程
        tryTerminate();
    }

shutdownNow

和shutdown不同的是,状态修改为STOP,不再执行队列中的任务; drainQueue此方法直接抛弃掉队列中的任务; 但是正在执行的任务将会正常完成;

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);
        interruptWorkers();
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}