Executors提供的基本线程池

2022-08-03  本文已影响0人  M_lear

一、newFixedThreadPool

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

创建固定数量的核心线程,使用LinkedBlockingQueue作为任务队列,没有限制容量,有内存溢出的风险。

二、newSingleThreadExecutor

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

等价于Executors.newFixedThreadPool(1)。

三、newCachedThreadPool

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

没有核心线程,都是非核心线程,线程的允许空闲时间为60秒,阻塞队列使用SynchronousQueue。

线程池提交任务的流程:

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        // 由于corePoolSize为0,这个if体的逻辑肯定不会执行
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // SynchronousQueue是否能offer成功取决于是否有线程在poll
        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);
        }
        // 如果没有线程在poll,则创建非核心线程执行任务
        else if (!addWorker(command, false))
            reject(command);
    }

由于newCachedThreadPool使用的是SynchronousQueue,导致情况比较复杂。
取决于任务的提交频率和任务的执行时间。
假如任务t1时间提交一次,任务执行时间为t2。

  1. 当t1-t2大于等于60秒,比如80秒提交一次,每次执行10秒,那么线程池的线程数不超过1,每来一个任务创建一个线程,执行完任务后闲置60秒被回收。此时已经失去了线程池的意义,线程无法复用。
  2. 当t1-t2小于60秒,那么线程闲置不会超过60秒,线程可以复用。
    2.1 当t1-t2小于60秒,但大于0秒时,线程池会稳定在只有一个线程。
    2.2 只有当t1-t2小于0秒时,即t1<t2时,线程池才会创建多个线程。

那么会不会创建大量的非核心线程导致内存溢出?
基本不会,这种情况,除非是t2特别特别大,t2>>>t1。

举个例子:
假如,1秒提交一次,一次执行1小时,线程池最终会稳定在多少个线程呢?
1小时等于3600秒。
那么前3600秒,由于之前的线程firstTask都还没执行完,所以每来一个任务就创建一个线程。当时间来到第3601秒,第一个创建出来的线程的firstTask执行完了,可以复用了,第3602秒,第二个创建出来的线程的firstTask执行完了,可以复用了,周而复始。
所以结论是,1小时后,线程池的线程数量会稳定在3600个,不会创建新的,旧的也不会被回收。

不适宜场景:间歇性的高频任务提交。短时间创建大量线程,然后又长时间无任务导致刚刚创建的大量线程被回收。

上一篇下一篇

猜你喜欢

热点阅读