ThreadPoolExecutor内的变量及方法解析

写在前面的话:

        我的blog里关于讲解线程池的文章还是比较多的,我转载的这边文章的主要原因是对ThreadPoolExecutor类里的一些主要属性和方法进行了详细讲解。文章开始对线程池的基本概念及线程池原理也进行了讲解,如果对这部分比较了解的同学可以直接略过。

一、概念


1.1 概念
​ 为了解决频繁请求创建线程,导致系统由于过度消耗内存或“切换过度”而导致系统资源不足的问题,就有了线程池的概念,线程池的核心逻辑是提前创建好若干个线程放在一个容器中。如果有任务需要处理,则将任务直接分配给线程池中的线程来执行就行,任务处理完以后这个线程不会被销毁,而是等待后续分配任务。

1.2 线程池工作原理
​ 一个线程从被提交(submit)到执行共经历以下流程:

线程池判断核心线程池里是的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下一个流程

线程池判断工作队列是否已满。如果工作队列没有满,则将新提交的任务储存在这个工作队列里。如果工作队列满了,则进入下一个流程。

线程池判断其内部线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已满了,则交给饱和策略来处理这个任务。

如图所示

在这里插入图片描述

二、优势

  2.1 降低创建线程和销毁线程的性能开销

  2.2 提高响应速度,当有新任务需要执行是不需要等待线程创建就可以立马执行

​  2.3 合理的设置线程池大小可以避免因为线程数超过硬件资源瓶颈带来的问题

三、ThreadPoolExecutor重要变量

3.1 ThreadPoolExecutor自身介绍
​ ThreadPoolExecutor继承自AbstractExecutorService,而AbstractExecutorService继承自ExecutorService。

3.2 Worker内部类
​ Worker继承了AQS,Worker包装了线程池内部的线程,AQS对于理解线程池有很大帮助.

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
        private static final long serialVersionUID = 6138294804551838833L;
        /**
         * Thread this worker is running in. Null if factory fails.
         */
        final Thread thread; //注意了,这才是真正执行 task 的线程,从构造函数可知是由ThreadFactury 创建的
        /**
         * Initial task to run. Possibly null.
         */
        Runnable firstTask; //这就是需要执行的 task
        /**
         * Per-thread task counter
         */
        volatile long completedTasks; //完成的任务数,用于线程池统计

        Worker(Runnable firstTask) {
            setState(-1); //初始状态 -1,防止在调用 runWorker(),也就是真正执行 task前中断 thread。
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

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

        ...
    }

3.2.1 每个 worker,都是一条线程,同时里面包含了一个 firstTask,即初始化时要被首先执行的任务.

​ 3.2.2 最终执行任务的,是 runWorker()方法

​ 3.2.3 Worker 类继承了 AQS,并实现了 Runnable 接口,注意其中的 firstTask 和 thread 属性: firstTask 用它来保存传入的任务;thread 是在调用构造方法时通过 ThreadFactory 来创建的线程,是用来处理任务的线程。

​ 在调用构造方法时,需要传入任务,并赋值给当前对象,即this.firstTask = firstTask,然后通过 getThreadFactory().newThread(this);来新建一个线程,newThread 方法传入的参数是 this,因为 Worker 本身继承了 Runnable 接口,也就是一个线程,所以一个 Worker 对象在调用start方法,即启动的时候会调用 Worker 类中的 run 方法执行方法体。

​ 3.2.4 Worker 继承了 AQS,使用 AQS 来实现独占锁的功能。为什么不使用 ReentrantLock 来实现呢?可以看到 tryAcquire 方法,它是不允许重入的,而 ReentrantLock 是允许重入的:

lock 方法一旦获取了独占锁,表示当前线程正在执行任务中;那么它会有以下几个作用

1.如果正在执行任务,则不应该中断线程;
2.如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断;
3.线程池在执行 shutdown 方法或 tryTerminate 方法时会调用 interruptIdleWorkers 方法来中断空闲的线程,interruptIdleWorkers 方法会使用 tryLock 方法来判断线程池中的线程是否是空闲状态
4.之所以设置为不可重入,是因为我们不希望任务在调用像 setCorePoolSize 这样的线程池控制方法时重新获取锁,这样会中断正在运行的线程。

3.3 ctl 变量

3.3.1 原子特性
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

3.3.2 设计理念
​ 作者通过巧妙的设计,将一个整型变量按二进制位分成两部分,分别表示两个信息。这样就可以用一次cas原子操作进行赋值,更容易保证在多线程环境下保证运行状态和线程数量的统一。

3.3.3 常用方法
3.3.3.1 runStateOf(int c)
线程池生命周期状态
3.3.3.2 workerCountOf(int c)
​ 工作线程数

3.3.3.3 ctlOf(int rs, int wc)
​ 将 runState 和 workerCount 两个变量(都是整型)打包成一个 ctl 变量。

 

3.4 构建线程池七大参数
3.4.1 int corePoolSize
​ 线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

3.4.2 int maximumPoolSize
​ 一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

3.4.3 long keepAliveTime
​ 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。

3.4.4 TimeUnit unit
​ 存活时间单位。

3.4.5 BlockingQueue workQueue
​ 保存执行任务的队列,jdk中提供了四种工作队列:

3.4.5.1 ArrayBlockingQueue
​ 基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

3.4.5.2 LinkedBlockingQuene
​ 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

3.4.5.3 SynchronousQuene
​ 一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

3.4.5.4 PriorityBlockingQueue
​ 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

3.4.6 ThreadFactory threadFactory
​ 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

3.4.7 RejectedExecutionHandler handler
​ 当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,开始执行拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

3.4.7.1 CallerRunsPolicy
​ 用调用者所在的线程来执行任务;

3.4.7.2 AbortPolicy
​ 直接抛出异常,默认策略;

3.4.7.3 DiscardPolicy
​ 直接丢弃任务;

3.4.7.4 DiscardOldestPolicy
​ 丢弃阻塞队列中靠最前的任务,并执行当前任务;

3.4.7.5 自定义策略
​ 根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

 

3.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; //所有的任务都已结束,线程数量为 0,处于该状态的线程池即将调用 terminated()方法
private static final int TERMINATED = 3 << COUNT_BITS;// terminated()方法执行完成


ThreadPoolExecutor中状态定义的值大小是有序的,即:TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING(最高位1是负数)。同时线程池状态转换如下图所示:

在这里插入图片描述

四、线程池重要方法

4.1 execute(Runnable command)

public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            int c = ctl.get();
            if (workerCountOf(c) < corePoolSize) {//1.当前池中线程比核心数少,新建一个线程执行任务
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
            if (isRunning(c) && workQueue.offer(command)) {//2.核心池已满,但任务队列未满,添加到队列中
                int recheck = ctl.get();
                //任务成功添加到队列以后,再次检查是否需要添加新的线程,因为已存在的线程可能被销毁了
                if (! isRunning(recheck) && remove(command))
                    reject(command);//如果线程池处于非运行状态,并且把当前的任务从任务队列中移除成功,则拒绝该任务
                else if (workerCountOf(recheck) == 0)//如果之前的线程已被销毁完,新建一个线程
                addWorker(null, false);
            }
            else if (!addWorker(command, false)) //3.核心池已满,队列已满,试着创建一个新线程
                reject(command); //如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务
        }

4.2 addWorker(Runnable firstTask, boolean core)

private boolean addWorker(Runnable firstTask, boolean core) {
        retry: //goto 语句,避免死循环
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            // Check if queue empty only if necessary.
            /*如果线程处于非运行状态,并且 rs 不等于 SHUTDOWN 且 firstTask 不等于空且且
            workQueue 为空,直接返回 false(表示不可添加 work 状态)
            1. 线程池已经 shutdown 后,还要添加新的任务,拒绝
            2. (第二个判断)SHUTDOWN 状态不接受新任务,但仍然会执行已经加入任务队列的任
            务,所以当进入 SHUTDOWN 状态,而传进来的任务为空,并且任务队列不为空的时候,是允许添加
            新线程的,如果把这个条件取反,就表示不允许添加 worker*/
            if (rs >= SHUTDOWN &&
                    ! (rs == SHUTDOWN &&
                            firstTask == null &&
                            ! workQueue.isEmpty()))
                return false;
            for (;;) { //自旋
                int wc = workerCountOf(c);//获得 Worker 工作线程数
                //如果工作线程数大于默认容量大小或者大于核心线程数大小,则直接返回 false 表示不
                能再添加 worker。
                if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))//通过 cas 来增加工作线程数,如果 cas 失败,则直接重试
                    break retry;
                c = ctl.get(); // Re-read ctl //再次获取 ctl 的值
                if (runStateOf(c) != rs) //这里如果不想等,说明线程的状态发生了变化,继续重试
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        
        //上面这段代码主要是对 worker 数量做原子+1 操作,下面的逻辑才是正式构建一个 worker


        boolean workerStarted = false; //工作线程是否启动的标识
        boolean workerAdded = false; //工作线程是否已经添加成功的标识
        Worker w = null;
        try {
            w = new Worker(firstTask); //构建一个 Worker,这个 worker 是什么呢?我们可以看到构造方法里面传入了一个 Runnable 对象
            final Thread t = w.thread; //从 worker 对象中取出线程
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock(); //这里有个重入锁,避免并发问题
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());
                    //只有当前线程池是正在运行状态,[或是 SHUTDOWN 且 firstTask 为空],才能添加到 workers 集合中
                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        //任务刚封装到 work 里面,还没 start,你封装的线程就是 alive,几个意思?肯定是要抛异常出去的
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w); //将新创建的 Worker 添加到 workers 集合中
                        int s = workers.size();
                        //如果集合中的工作线程数大于最大线程数,这个最大线程数表示线程池曾经出现过的最大线程数
                        if (s > largestPoolSize)
                            largestPoolSize = s; //更新线程池出现过的最大线程数
                        workerAdded = true;//表示工作线程创建成功了
                    }
                } finally {
                    mainLock.unlock(); //释放锁
                }
                if (workerAdded) {//如果 worker 添加成功
                    t.start();//启动线程
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w); //如果添加失败,就需要做一件事,就是递减实际工作线程数(还记得我们最开始的时候增加了工作线程数吗)
        }
        return workerStarted;//返回结果
    }

4.3 addWorkerFailed(Worker w)

addWorker 方法中,如果添加 Worker 并且启动线程失败,则会做失败后的处理。

private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }
    主要逻辑:
        1.如果 worker 已经构造好了,则从 workers 集合中移除这个 worker
        2.原子递减核心线程数(因为在 addWorker 方法中先做了原子增加)
        3.尝试结束线程池

4.4 runWorker(Worker w)

addWorker,主要作用是增加工作线程,而 Worker 简单理解其实就是一个线程,里面重新定义了 run 方法。线程增加成功后,调用了start方法,最终执行worker的run方法,run方法调用了runWorker 方法。

​ runWorker(Worker w)的大致逻辑:

4.4.1 如果 task 不为空,则开始执行 task;

​ 4.4.2 如果 task 为空,则通过 getTask()再去取任务,并赋值给 task,如果取到的 Runnable 不为空,则执行该任务;

​ 4.4.3 执行完毕后,通过 while 循环继续 getTask()取任务;

​ 4.4.4 如果 getTask()取到的任务依然是空,那么整个 runWorker()方法执行完毕。
 

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //unlock,表示当前 worker 线程允许中断,因为 new Worker 默认的 state=-1,此处是调用
        //Worker 类的 tryRelease()方法,将 state 置为 0,
        //而 interruptIfStarted()中只有 state>=0 才允许调用中断
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //注意这个 while 循环,在这里实现了 [线程复用] // 如果 task 为空,则通过getTask 来获取任务
            while (task != null || (task = getTask()) != null) {
                w.lock(); //上锁,不是为了防止并发执行任务,为了在 shutdown()时不终止正在运行的 worker
                //线程池为 stop 状态时不接受新任务,不执行已经加入任务队列的任务,还中断正在执行的任务
                //所以对于 stop 状态以上是要中断线程的
                //(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP)确保线程中断标志位为 true 且是 stop 状态以上,接着清除了中断标志
                //!wt.isInterrupted()则再一次检查保证线程需要设置中断标志位
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);//这里默认是没有实现的,在一些特定的场景中我们可以自己继承 ThreadpoolExecutor 自己重写
                    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 {
                        afterExecute(task, thrown); //这里默认默认而也是没有实现
                    }
                } finally {
                    //置空任务(这样下次循环开始时,task 依然为 null,需要再通过 getTask()取) + 记录该 Worker 完成任务数量 + 解锁
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
            //1.将入参 worker 从数组 workers 里删除掉;
            //2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组workers
        }
    }

4.5 getTask()

worker 线程会从阻塞队列中获取需要执行的任务,这个方法不是简单的 take 数据,如果线程数量大于核心线程数量,会调用 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)方法。

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {//自旋
            int c = ctl.get();
            int rs = runStateOf(c);
            /* 对线程池状态的判断,两种情况会 workerCount-1,并且返回 null
            1. 线程池状态为 shutdown,且 workQueue 为空(反映了 shutdown 状态的线程池还是要执行 workQueue 中剩余的任务的)
            2. 线程池状态为 stop(shutdownNow()会导致变成 STOP)(此时不用考虑 workQueue的情况)*/
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;//返回 null,则当前 worker 线程会退出
            }
            int wc = workerCountOf(c);
            // timed 变量用于判断是否需要进行超时控制。
            // allowCoreThreadTimeOut 默认是 false,也就是核心线程不允许进行超时;
            // wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
            // 对于超过核心线程数量的这些线程,需要进行超时控制
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            /*1. 线程数量超过 maximumPoolSize 可能是线程池在运行时被调用了 setMaximumPoolSize()
            被改变了大小,否则已经 addWorker()成功不会超过 maximumPoolSize
            2. timed && timedOut 如果为 true,表示当前操作需要进行超时控制,并且上次从阻塞队列中
            获取任务发生了超时.其实就是体现了空闲线程的存活时间*/
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
                /*根据 timed 来判断,如果为 true,则通过阻塞队列 poll 方法进行超时控制,如果在
                keepaliveTime 时间内没有获取到任务,则返回 null.
                否则通过 take 方法阻塞式获取队列中的任务*/
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                if (r != null)//如果拿到的任务不为空,则直接返回给 worker 进行处理
                    return r;
                timedOut = true;//如果 r==null,说明已经超时了,设置 timedOut=true,在下次自旋的时候进行回收
            } catch (InterruptedException retry) {
                timedOut = false;// 如果获取任务时当前线程发生了中断,则设置 timedOut 为false 并返回循环重试
            }
        }
    }

4.6 submit(Callable task)

submit(Callable task)来自于ThreadPoolExecutor的父类AbstractExecutorService,可以用于获得线程执行的结果。

public <T> Future<T> submit(Callable<T> task) {
         if (task == null) throw new NullPointerException();
         // 1. 将任务包装成 FutureTask  RunnableFuture<V> 继承了 Runnable, Future<V>
         RunnableFuture<T> ftask = newTaskFor(task);
         // 2. 交给执行器执行
         execute(ftask);
         return ftask;
     }

五、常用线程池

JUC对线程的封装主要体现在Executors 类中,常用的线程池有:

5.1 newFixedThreadPool

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

FixedThreadPool 的核心线程数和最大线程数都是指定值,也就是说当线程池中的线程数超过核心线程数后,任务都会被放到阻塞队列中。另外 keepAliveTime 为 0,也就是超出核心线程数量以外的线程空余存活时间。 而这里选用的阻塞队列是LinkedBlockingQueue,使用的是默认容量 Integer.MAX_VALUE,相当于没有上限
这个线程池执行任务的流程如下:

  1. 线程数少于核心线程数,也就是设置的线程数时,新建线程执行任务
  2. 线程数等于核心线程数后,将任务加入阻塞队列
  3. 由于队列容量非常大,可以一直添加
  4. 执行完任务的线程反复去队列中取任务执行
    用途:FixedThreadPool 用于负载比较大的服务器,为了资源的合理利用,需要限制当前线程数量

5.2 newSingleThreadExecutor

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

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

5.3 newCachedThreadPool

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

CachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程; 并且没有核心线程,非核心线程数无上限,但是每个空闲的时间只有 60 秒,超过后就会被回收。
它的执行流程如下:

1.没有核心线程,直接向 SynchronousQueue 中提交任务
2.如果有空闲线程,就去取出任务执行;如果没有空闲线程,就新建一个
3.执行完任务的线程有 60 秒生存时间,如果在这个时间内可以接到新任务,就可以继续活下去,否则就被回收

5.4 newScheduledThreadPool

//此线程池keepAliveTime参数为0,缓存对列为DelayedWorkQueue。
  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

六、常用问题

6.1 构建线程池的常见问题

阿里开发手册不建议使用线程池,手册上是说**线程池的构建不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。**用 newFixdThreadPool 或者 singleThreadPool.允许的队列长度为Integer.MAX_VALUE,如果使用不当会导致大量请求堆积到队列中导致 OOM 的风险而 newCachedThreadPool,允许创建线程数量为 Integer.MAX_VALUE,也可能会导致大量线程的创建出现 CPU 使用过高或者 OOM 的问题。


6.2 如何合理配置线程池的大小

线程池设定最佳线程数目 = ((线程池设定的线程等待时间+线程 CPU 时间)/线程 CPU 时间 )* CPU 数目

6.4 线程池的关闭

hreadPoolExecutor 提 供 了 两 个 方 法 , 用 于 线 程 池 的 关 闭 , 分 别 是 shutdown() 和shutdownNow(),其中:shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务 shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。
 

6.5 线程池容量的动态调整

ThreadPoolExecutor 提 供 了 动 态 调 整 线 程 池 容 量 大 小 的 方 法 : setCorePoolSize() 和 setMaximumPoolSize(),setCorePoolSize:设置核心池大小 setMaximumPoolSize。

6.6 设置线程池最大能创建的线程数目大小 任务缓存队列及排队策略

在前面我们提到了任务缓存队列,即 workQueue,它用来存放等待执行的任务。 workQueue 的类型为 BlockingQueue,通常可以取下面三种类型:

6.6.1 ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

​ 6.6.2 LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为 Integer.MAX_VALUE;

​ 6.6.3 SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

ownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。
 

转载地址https://blog.csdn.net/weixin_43823391/article/details/114259620

猜你喜欢

转载自blog.csdn.net/dhklsl/article/details/118389085