J.U.C之线程池-ThreadPoolExecutor

ThreadPoolExecutor

JDK1.8中对与ThreadPoolExecutor是这么定义的:

/**
* An {@link ExecutorService} that executes each submitted task using
* one of possibly several pooled threads, normally configured
* using {@link Executors} factory methods.
*
* <p>Thread pools address two different problems: they usually
* provide improved performance when executing large numbers of
* asynchronous tasks, due to reduced per-task invocation overhead,
* and they provide a means of bounding and managing the resources,
* including threads, consumed when executing a collection of tasks.
* Each {@code ThreadPoolExecutor} also maintains some basic
* statistics, such as the number of completed tasks.
*/

ThreadPoolExecutor通常使用工厂方法(Executors)来配置执行实例,使用线程池中的线程来执行每一个提交的任务。ThreadPoolExecutor提供了两个主要功能:减少调用每个线程的开销,提高性能;提供了一系列方法来管理资源,监控执行。

接下来将会基于这两点和ThreadPoolExecutor的源码,对ThreadPoolExecutor进行解析:

  • 线程池的内部状态
  • ThreadPoolExecutor的构造方法和参数说明
  • 工厂方法的实现和线程池的类型
  • 线程池任务的提交、执行、中断和停止
  • 阻塞队列的选择和任务执行的策略
  • RejectedExecutionHandler任务的拒绝策略

线程池的内部状态

public class ThreadPoolExecutor extends AbstractExecutorService {
    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;//高3位111 能够接收新任务,并对新添加的任务进行处理
    private static final int SHUTDOWN   =  0 << COUNT_BITS;//高3位000 不接受新的任务,但是可以对已经添加的任务进行处理
    private static final int STOP       =  1 << COUNT_BITS;//高3位001 不接收新的任务,不处理已经添加的任务,并且会中断正在处理的任务
    private static final int TIDYING    =  2 << COUNT_BITS;//高3位010 当前所有的任务已经终止,然后会执行钩子函数terminated()
    private static final int TERMINATED =  3 << COUNT_BITS;//高3位011 线程池彻底中止

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }
}

变量ctl定义为AtomicInteger类型,共32位大小,记录了“线程池中的任务数量”和“线程池的状态”两个信息,其中高3位表示"线程池状态",低29位表示"线程池中的任务数量。

构造方法

ThreadPoolExecutor提供了四个重载的构造函数,先看最全的一个:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

这个构造函数一共有7个参数,每个参数的含义如下:

  • corePoolSize

线程池中核心线程的数量。在提交任务时如果线程池中核心线程的数量小于corePoolSize,线程池会新建一个线程来执行任务,直到当前线程数等于corePoolSize。

如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。

  • maximumPoolSize

线程池中允许的最大线程池数。在提交任务如果线程池的阻塞队列已满且当前线程池中核心线程的数量小于maximumPoolSize,则会新建一个线程来执行任务。

另外,如果线程池所用的BlockingQueue是无界的LinkedBlockingQueue,那么该参数将没有实际意义。

  • keepAliveTime

线程池所允许的空闲时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间,这段时间由keepAliveTime和unit一起决定。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。

  • unit(TimeUnit)
  • workQueue(BlockingQueue<Runable>)

用来保存等待执行的任务的阻塞队列,等待的任务必须实现Runnable接口。BlockingQueue有七种实现和一种内部实现类,具体分析可以参考 https://www.cnblogs.com/CHMaple/p/9284583.html。

  • threadFactory

用于设置创建线程的工厂方法,默认使用Executors.defaultThreadFactory()方法,该方法返回了一个DefaultThreadFactory的实例。通过newThread()方法提供创建线程的功能,newThread()方法创建的线程都是“非守护线程”而且“线程优先级都是Thread.NORM_PRIORITY”。

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

Executors工厂方法也提供了另一个线程工厂方法privilegedThreadFactory,该方法返回了一个PrivilegedThreadFactory的实例,该方法创建的新线程和当前线程具有相同的权限。

   static class PrivilegedThreadFactory extends DefaultThreadFactory {
        private final AccessControlContext acc;
        private final ClassLoader ccl;

        PrivilegedThreadFactory() {
            super();
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                // Calls to getContextClassLoader from this class
                // never trigger a security check, but we check
                // whether our callers have this permission anyways.
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);

                // Fail fast
                sm.checkPermission(new RuntimePermission("setContextClassLoader"));
            }
            this.acc = AccessController.getContext();
            this.ccl = Thread.currentThread().getContextClassLoader();
        }

        public Thread newThread(final Runnable r) {
            return super.newThread(new Runnable() {
                public void run() {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            Thread.currentThread().setContextClassLoader(ccl);
                            r.run();
                            return null;
                        }
                    }, acc);
                }
            });
        }
    }

 

  • handler(RejectedExecutionHandler)

线程池的拒绝执行策略,是指将任务添加到线程池而线程池拒绝该任务时所要采取的执行策略。如果线程池的线程已经达到最大数量且阻塞队列也满了,那么线程池会根据构造函数的拒绝策略参数执行拒绝操作。

线程池默认提供了四种拒绝策略:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。

本文最后将会依次介绍每个策略的执行方式。

工厂方法和默认的线程池类型

工厂类Executors提供了三种默认的线程池:FixedThreadPool、SingleThreadPool、CachedThreadPool。

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

 FixedThreadPool是一个可重用固定数量线程的线程池。corePoolSize和maximumPoolSize相等且都为FixedThreadPool的构造方法的参数nThreads,意味着当线程池满而阻塞队列也满的时候将直接执行拒绝策略。

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

ThreadPoolExecutor的默认执行策略是AbortPolicy,默认会抛出RejectedExecutionException异常。

keepAliveTime设置为0L,意味着空闲即停止。

而workQueue使用的是LinkedBlockingQueue并且没有设置队列的最大容量,即为一个无界队列。使用无界队列之后,新的任务到达到时候,倘若线程数量已经等于corePoolSize,则会直接加入到workQueue中,那么maximumPoolSize参数将没有实际意义,实际线程数量将永远小于等于核心线程的数量,并且指定的拒绝策略也将失效,因为线程池不会拒绝任务。

使用FixedThreadPool的缺点也因此很明显,由于使用无界队列,所以当线程池的核心线程满载运行的速度低于主线程提交任务的速度时,会导致workQueue不断增长,最终可能耗尽内存存储资源。

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

作为单一worker线程的线程池,SingleThreadExecutor把corePool和maximumPoolSize均被设置为1,和FixedThreadPool一样使用的是无界队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样。

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

CachedThreadPool的核心线程数是0,最大线程数是Integer的最大值,同时BlockingQueue使用的是SynchronousQueue,这个阻塞队列的特点是没有容量,每一个put操作必须要等待一个take操作,否则不能添加元素。

所以当新的任务到来的时候,CachedThreadPool会重用一个空闲的线程执行任务或者创建一个新的线程来执行任务。keepAliveTime和unit的值分别是60和秒,意味着空闲的进程超过60秒就会被终止。

CachedThreadPool的优点是在线程资源和系统资源充足的情况下会尽可能快的完成任务,但是缺点也很明显,当线程的处理速度跟不上主线程提交任务的速度的时候,CachedThreadPool会不断创建新的线程来执行任务,最终可能导致系统耗尽CPU和内存资源。

所以使用CachedThreadPool的时候一定要注意控制并发的任务数,否则高并发的情况下可能会导致严重的性能问题。

 任务的提交

线程池根据任务的不同需求提供了两种方式提交任务,Executor.execute(Runnable command)、ExecutorService.submit(Callable<T> task)。以ThreadPoolExecutor对Executor.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);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

执行流程如下:

1、如果当前执行线程数小于核心线程数,那么调用addWorker创建新线程并执行任务,成功返回ture,否则执行步骤2;

2、如果线程处于RUNNING状态则尝试插入workQueue阻塞队列,如果插入成功则尝试recheck,如果失败则进入步骤三;

3、如果线程池不是RUNNING状态或者加入到阻塞队列失败,则会尝试创建非核心线程到线程池(不大于maximumPoolSize),如果失败则调用reject(command)执行约定的拒绝策略。

步骤二需要做recheck是因为需要防止在任务进入方法的时候线程池被中止导致任务错误执行。

判定成功之后会调用私有方法addWorker方法创建新线程执行任务:

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        // 获取当前线程状态,当不是RUNNING状态的时候,只有SHUTDOWN状态并且firstTask为空(不能添加新任务但是可以执行在队列里的任务)、阻塞任务队列非空的情况下可以新建线程继续执行任务
        int rs = runStateOf(c);
        if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
            return false;
        // 内层循环,worker + 1
        for (;;) {
            // 线程数量
            int wc = workerCountOf(c);
            // 如果当前线程数大于线程最大上限CAPACITY  return false
            // 若core == true,则与corePoolSize 比较,否则与maximumPoolSize ,大于 return false
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // worker + 1,成功跳出retry循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // CAS add worker 失败,再次读取ctl
            c = ctl.get();
            // 如果状态不等于之前获取的state,跳出内层循环,继续去外层循环判断
            if (runStateOf(c) != rs)
                continue retry;
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 新建线程:Worker
        w = new Worker(firstTask);
        // 当前线程
        final Thread t = w.thread;
        if (t != null) {
            // 获取主锁:mainLock
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 获取线程状态
                int rs = runStateOf(ctl.get());
                // rs < SHUTDOWN ==> 线程处于RUNNING状态
                // 或者线程处于SHUTDOWN状态,且firstTask == null(可能是workQueue中仍有未执行完成的任务,创建没有初始任务的worker线程执行)
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    // 当前线程已经启动,抛出异常
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // workers是一个HashSet<Worker>
                    workers.add(w);
                    // 设置最大的池大小largestPoolSize,workerAdded设置为true
                    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;
}

 

在这里需要好好查看addWorker中的参数,在execute()方法中,有三处调用了该方法:

第一次:workerCountOf(c) < corePoolSize ==> addWorker(command, true),这个很好理解,当然线程池的线程数量小于 corePoolSize ,则新建线程执行任务即可,在执行过程core == true,内部与corePoolSize比较即可。
第二次:加入阻塞队列进行Double Check时,else if (workerCountOf(recheck) == 0) ==>addWorker(null, false)。如果线程池中的线程==0,按照道理应该该任务应该新建线程执行任务,但是由于已经该任务已经添加到了阻塞队列,那么就在线程池中新建一个空线程,然后从阻塞队列中取线程即可。
第三次:线程池不是RUNNING状态或者加入阻塞队列失败:else if (!addWorker(command, false)),这里core == fase,则意味着是与maximumPoolSize比较。

在新建线程执行任务时,将Runnable包装成一个Worker,Worker为ThreadPoolExecutor的内部类,Worker的源码如下:

private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
    private static final long serialVersionUID = 6138294804551838833L;

    /** Thread this worker is running in.  Null if factory fails. Task所在的线程*/
    final Thread thread;
    /** Initial task to run.  Possibly null. 运行的任务执行Task*/
    Runnable firstTask;
    /** Per-thread task counter */
    volatile long completedTasks;

    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        //设置AQS的同步状态private volatile int state,是一个计数器,大于0代表锁已经被获取
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 利用ThreadFactory和 Worker这个Runnable创建的线程对象
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);//执行任务
    }
}

当线程启动调用Worker的run的时候,会进入到方法runWorker(this):

final void runWorker(Worker w) {
    // 当前线程
    Thread wt = Thread.currentThread();
    // 要执行的任务
    Runnable task = w.firstTask;
    w.firstTask = null;

    // 释放锁,运行中断
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            // worker 获取锁
            w.lock();
            // 确保只有当线程是stoping时,才会被设置为中断,否则清楚中断标示
            // 如果线程池状态 >= STOP ,且当前线程没有设置中断状态,则wt.interrupt()
            // 如果线程池状态 < STOP,但是线程已经中断了,再次判断线程池是否 >= STOP,如果是 wt.interrupt()
            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;
                // 完成任务数 + 1
                w.completedTasks++;
                // 释放锁
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

 注意到 这一步通过getTask()方法获得任务,通过processWorkerExit()方法执行下一步。

getTask()方法会根据线程池的超时策略判定是否需要超时控制而有选择地从workQueue中获取元素,有超时时间地poll或者阻塞地take任务。

processWorkerExit方法的源码如下:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // true:用户线程运行异常,需要扣减
    // false:getTask方法中扣减线程数量
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();//获取主锁
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);//从HashSet中移除worker,HashSet中存储着线程池中所有的工作线程
    } finally {
        mainLock.unlock();
    }

    // 有worker线程移除,可能是最后一个线程退出需要尝试终止线程池
    tryTerminate();

    int c = ctl.get();
    // 如果线程为running或shutdown状态,即tryTerminate()没有成功终止线程池,则判断是否有必要保留一个worker
    if (runStateLessThan(c, STOP)) {
        // 正常退出,计算需要维护的最小线程数量
        if (!completedAbruptly) {
            // allowCoreThreadTimeOut 是否需要维持核心线程的数量,默认false
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 如果min ==0 或者workerQueue为空,min = 1
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 如果线程数量大于最少数量min,直接返回,不需要新增线程
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        //如果线程池没有终止,那么线程池需要保持一定数量的线程,则添加一个没有firstTask的worker
        addWorker(null, false);
    }
}

当线程池涉及到要移除worker时候都会调用tryTerminate(),该方法主要用于判断线程池中的线程是否已经全部移除了,如果是的话则关闭线程池。

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 线程池处于Running状态
        // 线程池已经终止了
        // 线程池处于ShutDown状态,但是阻塞队列不为空
        if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;

        // 执行到这里,就意味着线程池要么处于STOP状态,要么处于SHUTDOWN且阻塞队列为空
        // 这时如果线程池中还存在线程,则会尝试中断线程
        if (workerCountOf(c) != 0) {
            //线程池还有线程,但是队列没有任务了,需要中断唤醒等待任务的线程
            //(runwoker的时候首先就通过w.unlock设置线程可中断,getTask最后面的catch处理中断)
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 尝试终止线程池
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    // 线程池状态转为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
    }
}

线程池的终止

线程池ThreadPoolExecutor提供了shutdown()和shutDownNow()用于关闭线程池。

shutdown():按过去执行已提交任务的顺序发起一个有序的关闭,并且不接受新任务。

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

shutdownNow():尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。

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;
}

 阻塞队列的选择和任务执行的策略

任务提交到执行的策略一般有三种:

  • 直接提交

工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。由于SynchronousQueue没有容量的特性,在某次添加元素阻塞队列之后必须等待其他线程取走元素后才能继续添加元素,如果不存在可用于立即运行任务的线程,将任务添加到执行队列的操作会失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁。(A2依赖于A1的结果,先提交A1再提交A2,A1必定先于A2执行)

直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。

当新的任务以超过队列所能处理的数量连续到达时,此策略允许无界线程池的执行线程数量具有增长的可能性。

  • 无界队列

使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)在所有 corePoolSize 线程都在执行任务的时候,新任务将会插入到队列中等待执行。这样,创建的执行线程的数量就不会超过 corePoolSize。因此,maximumPoolSize的值也就无效了。

当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程池的队列容量具有增长的可能性。

  • 有界队列

使用直接提交和无界队列的策略都有引发资源耗尽的风险,而使用有界队列(比如ArrayBlockingQueue或者有容量限制的LinkedBlockingQueue)有助于避免发生资源耗尽的情况,但是使用有界队列时线程池的参数会需要谨慎调整。队列的大小和线程池的大小需要相互折衷:使用大型队列和小型线程池可以降低CPU使用率、操作系统资源和上下文切换开销,但是可能会人为地降低吞吐量;而使用小型队列通常要求较大的线程池,会消耗更多的CPU资源,更频繁的上下文切换和更多的线程调度开销,可能会被动地降低吞吐量,而且如果任务频繁阻塞会导致更多的资源消耗。

任务的拒绝策略

RejectedExecutionHandler接口提供了对于拒绝任务方法的自定义执行的功能,ThreadPoolExecutor也提供了四种拒绝策略的实现类。

  • AbortPolicy

拒绝任务的时候抛出RejectedExecutionException异常,包含了执行线程的信息和线程池的信息。AbortPolicy也是线程池的默认策略。

 private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
/**
 * Always throws RejectedExecutionException.
 *
 * @param r the runnable task requested to be executed
 * @param e the executor attempting to execute this task
 * @throws RejectedExecutionException always
 */
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
  • CallerRunsPolicy

该策略会调用任务提交者所在的线程本身来执行任务。

/**
 * Executes task r in the caller's thread, unless the executor
 * has been shut down, in which case the task is discarded.
 *
 * @param r the runnable task requested to be executed
 * @param e the executor attempting to execute this task
 */
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}
  • DiscardOldestPolicy

丢弃阻塞队列里面最靠前的任务(poll方法是取出头部的元素),并执行当前的任务。

/**
 * Obtains and ignores the next task that the executor
 * would otherwise execute, if one is immediately available,
 * and then retries execution of task r, unless the executor
 * is shut down, in which case task r is instead discarded.
 *
 * @param r the runnable task requested to be executed
 * @param e the executor attempting to execute this task
 */
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}
  • DiscardPolicy

什么都不做,即丢弃任务。

/**
 * Does nothing, which has the effect of discarding task r.
 *
 * @param r the runnable task requested to be executed
 * @param e the executor attempting to execute this task
 */
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

 

转载于:https://www.cnblogs.com/CHMaple/p/9288537.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值