一 关于 ThreadPoolExecutor

.在jdk1.8中,标识线程状态的runState,用 AtomicInteger ctl 来表示。

线程池的7个核心参数

corePoolSize:核心线程数量
maximumPoolSize:最大线程数量
keepAliveTime:空闲线程存活时间
unit:存活时间的单位(分 秒--)
workQueue:工作队列
threadFactory:线程工厂,主要用来创建线程
handler:拒绝处理任务的策略

BlockingQueue<Runnable> workQueue 一个阻塞队列,用来存储等待执行的任务,包括以下3种队列:

  • ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
  • LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
  • synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

线程池的四种状态

static final int RUNNING    = 0;
static final int SHUTDOWN   = 1;
static final int STOP       = 2;
static final int TERMINATED = 3;

当创建线程池后,初始时,线程池处于RUNNING状态;
若调用shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
若调用shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

要知道任务提交给线程池之后的处理策略,这里总结一下主要有4点

  • 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
  • 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
  • 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
  • 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于 corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。 

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

• ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
• ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
• ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
• ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

线程池的关闭

    • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
    • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    二  Executors

    (阿里巴巴开发手册不建议使用,对线程池不熟悉的人会造成资源浪费、产生大量垃圾) 

    1.FixedThreadPool: 

      corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue

    适用于为了满足资源管理需求,而需要限制当前线程数量的应用场景。它适用于负载比较重的服务器;

       (1) .如果当前运行的线程数小于corePoolSize,则创建新的线程来执行任务;

        (2).当前运行的线程数等于corePoolSize后,将任务加入LinkedBlockingQueue;

        (3).线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue中获取任务来执行;

             使用无界队列 LinkedBlockingQueue(队列的容量为Intger.MAX_VALUE)

    java获取线程池当前队列中的数量 线程池获取队列长度_并发

     

    2. SingleThreadExecutor

    单个worker线程的Executor,corePoolSize和maximumPoolSize都被设置为1.也使用的LinkedBlockingQueue

     适用于需要保证顺序地执行各个任务并且在任意时间点,不会有多个线程是活动的应用场景。

    (1)如果当前运行的线程数少于corePoolSize,则创建一个新的线程执行任务;

    (2)当线程池中有一个运行的线程后,将任务加入LinkedBlockingQueue

    (3)线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue中获取任务来执行;

    使用无界队列LinkedBlockingQueue队列的容量为Intger.MAX_VALUE

    java获取线程池当前队列中的数量 线程池获取队列长度_并发_02

    3  CachedThreadPool

    适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器;

    corePoolSize被设置为空(0),maximumPoolSize被设置为Integer.MAX.VALUE,即它是无界的,这也就意味着如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新的线程(OOM)使用 SynchronousQueue队列(不存储元素的堵塞队列)

     (1).首先向队列提交任务(offer)。如果当前有闲线程正在向队列索要任务,那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成,否则执行下面的步骤2;

    (2)当初始线程池为空,或者maximumPool中没有空闲线程时,将没有线程执行poll来执行任务。这种情况下,步骤1将失败,此时CachedThreadPool会创建新线程执行任务,execute方法执行完成;

      

    java获取线程池当前队列中的数量 线程池获取队列长度_线程池_03

    4.ScheduledThreadPoolExecutor

    适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景,

    主要用来在给定的延迟后运行任务,或者定期执行任务。使用的任务队列DelayQueue(优先级队列)封装了一个PriorityQueue,PriorityQueue会对队列中的任务进行排序,执行所需时间短的放在前面先被执行(ScheduledFutureTask的time变量小的先执行),如果执行所需时间相同则先提交的任务将被先执行(ScheduledFutureTask的squenceNumber变量小的先执行)。

     (1). 当调用ScheduledThreadPoolExecutor的 scheduleAtFixedRate() 方法或者scheduleWirhFixedDelay() 方法时,会向ScheduledThreadPoolExecutor的 DelayQueue 添加一个实现了 RunnableScheduledFutur 接口的 ScheduledFutureTask 。

    ( 2 )   线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

     


    java获取线程池当前队列中的数量 线程池获取队列长度_应用场景_04

    ScheduledThreadPoolExecutor执行周期任务的步骤

    1.线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前系统的时间;

    2.线程1执行这个ScheduledFutureTask;

    3.线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间;

    4.线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。

    java获取线程池当前队列中的数量 线程池获取队列长度_线程池_05

    5.弊端

            FixedThreadPool SingleThreadExecutor : 允许请求的队列长度最大为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。

            CachedThreadPool ScheduledThreadPool : 允许创建的线程数量最大为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。

     

    三 其他

    1.线程池的 execute() 和 submit() 方法都是向线程池提交一个任务,交由线程池去执行。区别在于submit()方法可以返回任务执行的结果,底层调用的仍然是 execute() 方法。

                               

    java获取线程池当前队列中的数量 线程池获取队列长度_应用场景_06