线程池类ThreadPoolExecutor解析

1、使用线程池的好处

线程使应用能够更加充分合理地协调利用CPU、内存、网络、I/O等系统资源。线程的创建需要开辟虚拟机栈、程序计数器、本地方法栈等线程私有的内存空间。在线程销毁时需要回收这些资源。频繁地创建和销毁线程会浪费大量的系统资源,增加并发编程风险。另外,在服务器负载过大的时候,如何让新线程等待或者友好地拒绝服务?这都是线程自身无法解决的。所以需要通过线程池协调多个线程,并实现类似主次线程隔离、定时执行、周期执行等任务。线程池的作用包括:

  • 利用线程池管理并复用线程、控制最大并发数。
  • 实现任务线程队列缓存策略和拒绝机制。
  • 实现某些与实践相关的功能,如定时执行、周期执行等。
  • 隔离线程环境。比如,交易服务和搜索服务在同一台服务器上,分别开启两个线程池,交易线程的资源消耗明显要大;因此,通过配置独立的线程池,将较慢的交易服务与搜索服务隔离开,避免各服务线程相互影响。

2、ThreadPoolExecutor

了解线程池的基本作用后,我们学习一下线程池是如何创建线程的。首先从ThreadPoolExecutor构造方法讲起,学习如何自定义ThreadFactory和RejectedExecutionHandler,并编写一个最简单的线程池示例。然后通过分析ThreadPoolExecutor的execute和addWorker两个核心方法,学习如何把任务线程加入到线程池中运行。

ThreadPoolExecutor构造方法如下:

public ThreadPoolExecutor(
        int corePoolSize,                          //参数1
        int maximumPoolSize,                       //参数2
        long keepAliveTime,                       //参数3
        TimeUnit unit,                            //参数4
        BlockingQueue<Runnable> workQueue,        //参数5
        ThreadFactory threadFactory,              //参数6
        RejectedExecutionHandler handler) {       //参数7
        if (corePoolSize < 0 ||
                //maximumPoolSize必须大于或等于1,也要大于或等于corePoolSize(第一处)
                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;
    }
  1. 参数1,corePoolSize:表示常驻核心线程数。如果等于0,则任务执行完成以后,没有任何请求进入时销毁线程池的线程;如果大于0,即使本地任务执行完毕,核心线程也不会被销毁。这个值的设置非常关键,设置过大会浪费资源,设置过小会导致线程频繁地创建或销毁。
  2. 参数2,maximumPoolSize:表示线程池能够容纳同时执行的最大线程数。从上方示例代码中的第一处来看,必须大于或等于1.如果待执行的线程数大于此值,需要借助第五个参数的帮助,缓存在队列中。如果maximumPoolSize与corePoolSize相等,即是固定大小线程池。
  3. 参数3,keepAliveTime:表示线程池中线程空闲时间,当空闲时间达到keepAliveTime值时,线程会被销毁,直到只剩下corePoolSize个线程为止,避免浪费内存和句柄资源。在默认情况下,当线程池的线程数大于corePoolSize时,keepAliveTime才会起作用。但是当ThreadPoolExecutor的allowCoreThreadTimeOut变量设置为true时,核心线程超时后也会被回收
  4. 参数4,TimeUnit:表示时间单位。KeepAliveTime的时间单位通常是TimeUnit.SECONDS。
  5. 参数5,workQueue:表示缓存队列。当请求的线程数大于corePoolSize时,线程进入BlockingQueue阻塞队列(请注意,是当corePoolSize不够用时,将任务加入缓存队列,当缓存队列也容纳不下任务时,再开辟新的线程来处理任务,直到线程数到达maximumPoolSize)。后续示例代码中使用的LinkedBlockingQueue是单向链表,使用锁来控制入队和出队的原子性,两个锁分别控制元素的添加和获取,是一个生产消费模型队列。
  6. 参数6,threadFactory表示线程工厂。它用来生产一组相同任务的线程。线程池的命名是通过给这个factory增加组名前缀来实现的。在虚拟机栈分析时,就可以知道线程任务是由哪个线程工厂产生的。
  7. 参数7,handler表示执行拒绝策略的对象。当第五个参数workQueue的任务缓存区到达上限后,并且活动线程数等于maximumPoolSize的时候,线程池通过该策略处理请求,这是一种简单的限流保护。友好地拒绝策略可以是如下三种:保存到数据库进行削峰填谷,在空闲时再提取出来执行、转向某个提示页面、打印日志

从代码中的第二处来看,队列、线程工厂、拒绝处理服务都必须有示例对象,但在实际编程中,很少有程序员对这三者进行实例化,而是通过Executors这个线程池静态工厂提供默认实现,那么Executors与ThreadPoolExecutor是什么关系呢?(从源码上看,ThreadPoolExecutor是Executors的底层实现)线程池相关类图如下所示:

ExecutorService接口继承了Executor接口,定义了管理线程任务的方法。ExecutorService的抽象类AbstractExecutorService提供了submit()、invokeAll()等部分方法的实现。但是核心方法Executor.execute()并没有在这里实现。因为所有的任务都在这个方法里执行,不同实现会带来不同的执行策略。通过Executor的静态工厂方法可以创建三个线程池的包装对象:ForkJoinPool、ThreadPoolExecutor、ScheduledThreadPoolExecutor。

Executors(Java提供用来创建线程池的类,在JUC包下)的核心方法有五个(这五个方法每个都有多种重载方法,我只选取了一种):

  1. Executor.newWorkStealingPool:JDK8引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争,此构造方法中把CPU数量设置为默认的并行度。
         public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);
        }

     

  2. Executors.newCachedThreadPool:maximumPoolSize最大可以至Integer.MAX_VALUE,是高度可伸缩的线程池,如果达到这个上限,相信没有任何服务器能够继续工作,肯定会抛出OOM异常。keepAliveTime默认60秒,工作线程处于空闲状态,则回收工作线程。如果任务数增加,再次创建出新线程处理任务。
        public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>(),
                                          threadFactory);
        }

     

  3. Executors.newScheduledThreadPool:线程数最大值Integer.MAX_VALUE,与上述相同,存在OOM风险。它是ScheduledExecutorService接口家族的实现类,支持定时及周期性任务执行。相比Timer,ScheduledExecutorService更安全,功能更加强大,与newCachedThreadPool的区别是不回收工作线程。
         public static ScheduledExecutorService newScheduledThreadPool(
                int corePoolSize, ThreadFactory threadFactory) {
            return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
        }

     

  4. Executors.newSingleThreadExecutor:创建一个单线程的线程池,相当於单线程串行执行所有任务,保证按任务的提交顺序依次执行。
        public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>(),
                                        threadFactory));
        }

     

  5. Executors.newFixedThreadPool:输入的参数即是固定线程数,即是核心线程数也是最大线程数,不存在空闲线程,所以keepAliveTime等于0:
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    //这里输入的队列没有指明长度,看一下LinkedBlockingQueue的构造方法
    public LinkedBlockingQueue(){
        this(Integer.MAX_VALUE);
    }
    //使用这样的无界队列,如果瞬间请求量很大的话,会有OOM风险

     

除了newWorkStealingPool外,其他四个创建方式都存在资源耗尽的风险。

Executors中默认的线程工厂(threadFactory)和拒绝策略(handler)过于简单,通常对用户来说都不太友好,线程工厂需要做创建前的准备工作,对线程池创建的线程必须明确标识,就像药品的生产批号一样,为线程本身指定有意义的名称和相应的序列号。拒绝策略应该考虑到业务场景,返回相应的提示或者友好地跳转,以下为简单的ThreadFactory示例:

package Test;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class UserThreadFactory implements ThreadFactory {
    
    //name prefix译为名称前缀
    private final String namePrefix;
    
    //AtomicInteger是一个原子操作类,能够保证操作的原子性
    private final AtomicInteger nextId = new AtomicInteger();

    //定义线程组名称whatFeatureOfGroup,在使用jstack来排查线程问题时,非常有帮助
    UserThreadFactory(String whatFeatureOfGroup){
        this.namePrefix = "UserThreadFactory's" + whatFeatureOfGroup + "-Worker-";
    }

    @Override
    public Thread newThread(Runnable task) {
        String name = namePrefix + nextId.getAndIncrement();
        Thread thread = new Thread(null, task, name, 0, false);
        System.out.println(thread.getName());
        return thread;
    }
}

//任务执行体
class Task implements Runnable{

    private final AtomicLong count = new AtomicLong(0L);

    @Override
    public void run() {
        System.out.println("running_" + count.getAndIncrement());
    }
}

这个示例包括线程工厂和任务执行体的定义,通过newThread方法快速、统一地创建线程任务,强调线程一定要有特定意义的名称,方便出错时回溯。

简单地实现一下RejectedExecutionHandler,实现了接口的rejectedExecution方法,打印出当前线程池状态:

public class UserRejectHandler implements RejectedExecutionHandler{
    @Override
    public void rejectedExecution(Runnable task, ThreadPoolExecutor executor){
        System.out.println("task rejected." + executor.toString());
    }
}

当一个任务Task欲通过threadPoolexecutor.execute(Runnable)添加到线程中时,会出现以下几种情况:

  1. 此时线程池中的线程数小于corePoolSize(可以通过设置参数让核心线程也空闲超时销毁)时,即使线程池中的线程都处于空闲状态,那么也要创建一个新的线程来执行这个任务Task;
  2. 此时线程池中线程数量等于corePoolSize,但是缓冲队列workQueue未满,则将任务加入缓存队列等待处理;
  3. 此时线程池中线程数量大于corePoolSize,缓冲队列workQueue已满,但是线程数量小于maximumPoolSize时,则新建一个线程来执行此任务Task;
  4. 此时线程池中线程数量大于corePoolSize,缓冲队列workQueue已满,且线程数量等于maximumPoolSize时,那么通过指定的handler来对该任务Task执行拒绝策略。

handler有四个选择,在ThreadPoolExecutor中提供了四个公开的静态内部类:

  • AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。
        public static class AbortPolicy implements RejectedExecutionHandler {
            /**
             * Creates an {@code AbortPolicy}.
             */
            public 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());
            }
        }

     

  • DiscardPolicy:丢弃任务,但不抛出异常,这是不推荐的做法。
        public static class DiscardPolicy implements RejectedExecutionHandler {
            /**
             * Creates a {@code DiscardPolicy}.
             */
            public 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) {
            }
        }

     

  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中。
        public static class DiscardOldestPolicy implements RejectedExecutionHandler {
            /**
             * Creates a {@code DiscardOldestPolicy} for the given executor.
             */
            public DiscardOldestPolicy() { }
    
            /**
             * 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);
                }
            }
        }

     

  • CallerRunsPolicy:调用任务的run()方法绕过线程池直接执行。
        public static class CallerRunsPolicy implements RejectedExecutionHandler {
            /**
             * Creates a {@code CallerRunsPolicy}.
             */
            public 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();
                }
            }
        }

     

使用方式如下代码所示:

RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

根据之前实现的线程工厂和拒绝策略,一个简单线程池的相关示例代码实现如下:

package Test;


import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class UserThreadPool {
    public static void main(String[] args) {
        //缓存队列设置固定长度为2,为了快速触发rejectHandler
        BlockingQueue queue = new LinkedBlockingQueue(2);

        //假设外部任务线程的来源有机房1和机房2混合调用
        UserThreadFactory f1 = new UserThreadFactory("第1机房");
        UserThreadFactory f2 = new UserThreadFactory("第2机房");

        UserRejectHandler handler = new UserRejectHandler();

        //核心线程为1,最大线程为2,为了保证触发rejectHandler
        ThreadPoolExecutor threadPoolFirst = new ThreadPoolExecutor(
                1, 2, 60, TimeUnit.SECONDS, queue, f1, handler);
        //利用第二个线程工厂实例创建第二个线程池
        ThreadPoolExecutor threadPoolSecond = new ThreadPoolExecutor(
                1, 2, 60, TimeUnit.SECONDS, queue, f2, handler);

        //创建400个任务线程
        Runnable task = new Task();
        for (int i = 0; i < 200; i++){
            threadPoolFirst.execute(task);
            threadPoolSecond.execute(task);
        }
    }
}

运行结果如下:

From UserThreadFactory's 第1机房 -Worker-1
From UserThreadFactory's 第2机房 -Worker-1
From UserThreadFactory's 第1机房 -Worker-2
From UserThreadFactory's 第2机房 -Worker-2
running_2
running_3
running_4
running_5
running_0
running_1
your task is reject. java.util.concurrent.ThreadPoolExecutor@1396fbe[Running, pool size = 2, active thread = 2,
 queued tasks =2,completed task =1]

当任务被拒绝时,拒绝策略会打印出当前线程池的大小已经达到了maximumPoolSize=2,且队列已满,完成的任务数提示已经有一个(最后一行)。

3、ThreadPoolExecutor部分源码解析(execute()和addWorker())

在ThreadPoolExecutor的属性定义中频繁地用位移运算来表示线程池的状态,位移运算是改变当前值的一种高效手段,包括左移和右移,下面从属性定义开始阅读源码:

//Integer共有32位,最右边29位表示工作线程数,最左边3位表示线程池状态
//注:简单地说,3个二进制位可以表示从0到7的八个不同数值(第1处)    
private static final int COUNT_BITS = Integer.SIZE - 3;

//000-11111111111111111111111111111,类似于子网掩码,用于位的与运算,
//得到左边3位,还是右边29位
private static final int CAPACITY = (1 << COUNT_BITS) - 1;

//用左边3位,实现5种线程池状态。
//111-00000000000000000000000000000,十进制:-536,870,912。
//此状态表示线程池能够接受新任务
private static final int RUNNING = -1 << COUNT_BITS;

//000-00000000000000000000000000000,十进制:0。
//此状态表示不再接受新任务,但可以继续执行队列中的任务
private static final int SHUTDOWN  =  0 << COUNT_BITS;

//001-00000000000000000000000000000,十进制:536,870,912。
//此状态表示全面拒绝,并中断正在处理的任务
private static final int STOP  =  1 << COUNT_BITS;

//010-00000000000000000000000000000,十进制:1,073,741,824。
//此状态表示所有任务已经被终止
private static final int TIDYING  =  2 << COUNT_BITS;

//011-00000000000000000000000000000,十进制:1,610,612,736。
//此状态表示已清理完现场
private static final int TERMINATED  =  3 << COUNT_BITS;

//与运算,比如001-00000000000000000000000000011,表示67个工作线程,
//掩码取反:111-00000000000000000000000000000,即得到左边3位001,
//表示线程池当前处于STOP状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }

//同理掩码000-111111111111111111111111111111,得到右边29位,即工作线程数
private static int workerCountOf(int c)  { return c & CAPACITY; }

//把左边3位与右边29位按或运算,合并成一个值    
private static int ctlOf(int rs, int wc) { return rs | wc; }

第1处说明:线程池的状态用高3位表示,其中包括了符号位。五种状态的十进制值按从小到大依次排序为:

RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED。

这样设计的好处是可以通过比较值的大小来确定线程池的状态,例如程序中经常出现isRunning的判断:

private static boolean isRunning(int c){
    return c < SHUTDOWN;
}

Executor接口有且只有一个方法execute,通过参数传入待执行线程的对象,下面分析ThreadPoolExecutor关于execute方法的实现:

    public void execute(Runnable command) {
        //JDK中,command的解释为:@param command the runnable task
        if (command == null)
            throw new NullPointerException();

        //返回包含线程数及线程池状态的Integer类型数值
        int c = ctl.get();
        //如果工作线程数小于核心线程数,则创建线程任务并执行
        if (workerCountOf(c) < corePoolSize) {
            //addWorker是另一个极为重要的方法,见下一段源码解析(第1处)
            if (addWorker(command, true))
                return;
            //如果创建失败,防止外部已经在线程池中加入新的任务,重新获取一下
            c = ctl.get();
        }

        //只有当线程池处于RUNNING状态,才执行后半句:置入队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //如果线程池不是RUNNING状态,则将刚加入队列的任务移除
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //如果之前的线程已被消费完,新建一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //核心池和队列都已满,尝试创建一个新线程
        else if (!addWorker(command, false))
            //如果addWorker返回是false,即创建失败,则唤醒拒绝策略(第2处)
            reject(command);
    }
  • 第1处:execute方法在不同的阶段有三次addWorker的尝试动作。
  • 第2处:发生拒绝的理由有两个:(1)线程池状态为非RUNNING状态;(2)等待队列已满。

下面分析addWorker方法的源码:

/**
     *根据当前线程池状态,检查是否可以添加新的任务线程,如果可以则创建并启动任务
     * 如果一切正常且返回true。返回false的可能性如下:
     * 1、线程池没有处于RUNNING状态;
     * 2、线程工厂创建新的任务线程失败
     *
     * firstTask:外部启动线程池时所需要构造的第一个线程,它是线程的母体
     * core:新增工作线程时的判断指标,解释如下
     *      true  表示新增工作线程时,需要判断当前RUNNING状态的线程是否少于corePoolSize
     *      false 表示新增工作线程时,需要判断当前RUNNING状态的线程是否少于maximumPoolSize
     */
    private boolean addWorker(Runnable firstTask, boolean core) {
        //不需要任务预定义的语法标签,响应下文的continue retry,快速推出多层嵌套循环(第1处)
        retry:
        for (;;) {
            //获取线程池状态及线程数
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //如果是STOP及以上的状态,或者firstTask初始线程不为空,或者队列为空都会直接返回创建失败(第2处)
            if (rs >= SHUTDOWN &&
                    ! (rs == SHUTDOWN &&
                            firstTask == null &&
                            ! workQueue.isEmpty()))
                return false;
            
            for (;;) {
                //如果超过最大允许线程数则不能再添加新的线程
                //最大线程数不能超过2^29,否则影响左边3位的线程池状态值
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //将当前活动线程数+1(第3处)
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //线程池状态和工作线程数是可变化的,需要经常提取这个最新值
                c = ctl.get();  // Re-read ctl
                //如果已经关闭,则再次从retry标签处进入,在第2处再做判断(第4处)
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
                //如果线程还是处于RUNNING状态,那就在说明仅仅是第3处失败
                //继续循环执行(第5处)
            }
        }

        //开始创建工作线程
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //利用Worker构造方法中的线程池工厂创建线程,并封装成工作线程Worker对象
            w = new Worker(firstTask);
            //注意这是Worker中的属性对象thread(第6处)
            final Thread t = w.thread;
            if (t != null) {
                //在进行ThreadPoolExecutor的敏感操作时都需要持有主锁
                //避免在添加和启动线程时被干扰
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    //当线程池状态为RUNNING或SHUTDOWN
                    //且firstTask初始线程为空时
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        //整个线程池在运行期间的最大并发任务个数
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //终于看到亲切的start()方法
                    //注意,并非线程池的execute的command参数指向的线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                //线程启动失败,把刚才第3处加上的工作线程计数再减回去
                addWorkerFailed(w);
        }
        return workerStarted;
    }
  • 第1处:配合循环语句出现的label,类似于goto作用。label定义时,必须把标签和冒号的组合语句紧紧相邻定义在循环体之前,否则会编译出错。目的是实现多重循环时能够快速推出到任何一层。这种做法的出发点似乎非常贴心,但是在大型软件项目中,滥用标签行跳转的后果将是灾难性的。示例代码中,在retry下方有两个无限循环,在workerCount加1成功后,直接推出两层循环。
  • 第3处:与第1处的标签呼应,AtomicInteger对象的加1操作时原子性的。break retry表示直接跳出与retry相邻的这个循环体。
  • 第4处:此continue跳转至标签处,继续执行循环。如果条件为假,则说明线程池还处于运行状态,即继续在for(;;)循环内执行。
  • 第5处:compareAndIncrementWorkerCount方法执行失败的概率非常低。即使失败,再次执行时成功的概率也是极高的,类似于自旋锁原理。这里的处理逻辑是先加1,创建失败减1,这是轻量级处理并发创建线程的方式。如果先创建线程,成功再加1,当发现超出限制后再销毁线程,那么这样的处理方式明显比前者代价要大。
  • 第6处:Worker对象时工作线程的核心类实现,部分源码如下:
    //它实现Runnable接口,并把本对象作为参数输入给run()方法中的runWorker(this),
    //所以内部属性线程thread在start的时候,即会调用runWorker方法
    private final class Worker extends AbstarctQueueSynchronizer implements Runnable{
        Worker(Runnable firstTask){
            //它是AbstractQueueSynchronizer的方法
            //在runWorker方法执行之前禁止线程被中断
            setState(-1);
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
    
        //当thread被start()之后,执行runWorkder的方法
        public void run(){
            runWorker(this);
        }
    }

     

码出高效中作者的建议:使用线程池要注意以下几点:

  1. 合理设置各类参数,应根据实际业务场景来说设置合理地工作线程数。
  2. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
  3. 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。

阿里的Java开发手册中明确提出:线程池不允许使用Executors,而是通过ThreadPoolExecutor的方式创建,这样的处理方式能更加明确线程池的运行规则,规避资源耗尽的风险(Executors不能指定所有的参数,默认参数可能会造成资源浪费)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章