美文网首页
Executors提供的基本线程池

Executors提供的基本线程池

作者: M_lear | 来源:发表于2022-08-03 18:38 被阅读0次

    一、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个,不会创建新的,旧的也不会被回收。

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

    相关文章

      网友评论

          本文标题:Executors提供的基本线程池

          本文链接:https://www.haomeiwen.com/subject/wbxewrtx.html