zoukankan      html  css  js  c++  java
  • java 线程池

    ThreadPoolExecutor与ForkJoinPool区别在于前者每个线程共享队列,后者每个线程有各自的队列

    一 入参

    corePoolSize: 线程池核心线程数,当初始化线程池时,会创建核心线程进入等待状态,即使它是空闲的,核心线程也不会被摧毁,从而降低了任务一来时要创建新线程的时间和性能开销。当allowCoreThreadTimeOut手动设置为true,才会销毁

    maximumPoolSize: 最大线程数,意味着核心线程数都被用完了,那只能重新创建新的线程来执行任务,但是前提是不能超过最大线程数量,否则该任务只能进入阻塞队列进行排队等候,直到有线程空闲了,才能继续执行任务

    keepAliveTime: 线程存活时间,除了核心线程外,那些被新创建出来的线程可以存活多久。意味着,这些新的线程一但完成任务,而后面都是空闲状态时,就会在一定时间后被摧毁

    threadFactory:就是创建线程的线程工厂

    unit: 线程存活时间单位
    workQueue: 表示任务的阻塞队列,由于任务可能会有很多,而线程就那么几个,所以那么还未被执行的任务就进入队列中排队,队列我们知道是 FIFO 的,等到线程空闲了,就以这种方式取出任务。这个一般不需要我们去实现。handler的拒绝策略有四种:
      第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满
      第二种DisCardPolicy:不执行新任务,也不抛出异常
      第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行
      第四种CallerRunsPolicy:直接调用execute来执行当前任务

    1. LinkedBlockingQueue
      对于 FixedThreadPool 和 SingleThreadExector 而言,它们使用的阻塞队列是容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue,可以认为是无界队列。由于 FixedThreadPool 线程池的线程数是固定的,所以没有办法增加特别多的线程来处理任务,这时就需要 LinkedBlockingQueue 这样一个没有容量限制的阻塞队列来存放任务。这里需要注意,由于线程池的任务队列永远不会放满,所以线程池只会创建核心线程数量的线程,所以此时的最大线程数对线程池来说没有意义,因为并不会触发生成多于核心线程数的线程。
    2. SynchronousQueue
      第二种阻塞队列是 SynchronousQueue,对应的线程池是 CachedThreadPool。线程池 CachedThreadPool 的最大线程数是 Integer 的最大值,可以理解为线程数是可以无限扩展的。CachedThreadPool 和上一种线程池 FixedThreadPool 的情况恰恰相反,FixedThreadPool 的情况是阻塞队列的容量是无限的,而这里 CachedThreadPool 是线程数可以无限扩展,所以 CachedThreadPool 线程池并不需要一个任务队列来存储任务,因为一旦有任务被提交就直接转发给线程或者创建新线程来执行,而不需要另外保存它们。
    我们自己创建使用 SynchronousQueue 的线程池时,如果不希望任务被拒绝,那么就需要注意设置最大线程数要尽可能大一些,以免发生任务数大于最大线程数时,没办法把任务放到队列中也没有足够线程来执行任务的情况。
    3. DelayedWorkQueue
      第三种阻塞队列是DelayedWorkQueue,它对应的线程池分别是 ScheduledThreadPool 和 SingleThreadScheduledExecutor,这两种线程池的最大特点就是可以延迟执行任务,比如说一定时间后执行任务或是每隔一定的时间执行一次任务。DelayedWorkQueue 的特点是内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构。之所以线程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 选择 DelayedWorkQueue,是因为它们本身正是基于时间执行任务的,而延迟队列正好可以把任务按时间进行排序,方便任务的执行。

     

    二 线程池介绍

    FixedThreadPool(建固定核心数的线程池):定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程,而且它们的线程数存活时间都是无限的,并发执行

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

    对比 newSingleThreadPool,其实改变的也就是可以根据我们来自定义线程数的操作,比较相似。我们通过newFixedThreadPool(2)给它传入了 2 个核心线程数

    WorkStealingPool(创建一个具有抢占式操作的线程池):JDK1.8 版本加入的一种线程池,stealing 翻译为抢断、窃取的意思,它实现的一个线程池和之前4种都不一样,用的是 ForkJoinPool 类,构造函数代码如下

        public static ExecutorService newWorkStealingPool(int parallelism) {
            return new ForkJoinPool
                (parallelism,
                 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                 null, true);
        }

    parallelism => Runtime.getRuntime().availableProcessors() - 这是JVM可用的处理器数。
    handler => ForkJoinPool.defaultForkJoinWorkerThreadFactory - 返回新线程的默认线程工厂。
    asyncMode => true – 使其在aysnc模式下工作,并为分叉的任务设置FIFO顺序,这些任务永远不会从其工作队列中加入
      最明显的用意就是它是一个并行的线程池,参数中传入的是一个线程并发的数量,这里和之前就有很明显的区别,前面4种线程池都有核心线程数、最大线程数等等,而这就使用了一个并发线程数解决问题。从介绍中,还说明这个线程池不会保证任务的顺序执行,也就是 WorkStealing 的意思,抢占式的工作

    SingleThreadPool(创建单核心的线程池):单核心线程池,最大线程也只有一个,这里的时间为 0 意味着无限的生命,就不会被摧毁了,适用于有顺序的任务的应用场景

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

     

    CachedThreadPool(创建一个自动增长的线程池):可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,默认60秒,适用于耗时少,任务量大的情况

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

    ScheduledThreadPool(创建一个按照计划规定执行的线程池):周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务

        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue());
        }

    内部有一个延时的阻塞队列来维护任务的进行,延时也就是在这里进行的。我们把创建 newScheduledThreadPool 的代码放出来,这样对比效果图的话,显得更加直观。

    // 参数2:延时的时长
    scheduledExecutorService.schedule(th_all_1, 3000, TimeUnit.MILLISECONDS);
    scheduledExecutorService.schedule(th_all_2, 2000, TimeUnit.MILLISECONDS);
    scheduledExecutorService.schedule(th_all_3, 1000, TimeUnit.MILLISECONDS);
    scheduledExecutorService.schedule(th_all_4, 1500, TimeUnit.MILLISECONDS);
    scheduledExecutorService.schedule(th_all_5, 500, TimeUnit.MILLISECONDS);

    FAQ:

    1 线程池的核心线程会销毁吗?
      当allowCoreThreadTimeOut手动设置为true或者执行的run方法抛出异常,核心线程都会被销毁,但是后者还是会创建新的线程称呼来,前者则销毁什么都不做,关键在于allowCoreThreadTimeOut为true则下面代码直接返回,不在执行addWorker方法

    2 线程池的执行流程?



  • 相关阅读:
    PCI 设备详解二
    PCI 设备详解一
    SKBUFFER详解
    windows中的进程和线程
    sVIrt概述
    qemu网络虚拟化之数据流向分析二
    在VC6的debug框里面输出版权信息
    [yii]Trying to get property of non-object
    yii使用CFrom调用ajax失败的记录
    VC代码生成里面的/MT /MTd /MD /MDd的意思
  • 原文地址:https://www.cnblogs.com/hzzjj/p/15125779.html
Copyright © 2011-2022 走看看