一、线程池简介    

    在Java中,使用线程来异步执行任务。Java线程的创建与销毁需要一定的开销,如果我们为每个任务创建一个新的线程来执行,这些线程的创建与销毁将消耗大量的计算资源。Java的线程在此情况下,既是工作单元,也是执行机制。为了减少创建线程的额外开支,将工作单元与执行机制分离不失为一个好办法。由此,Java线程池自然的被设计出了。

    合理地使用线程池带来极大的好处:

    1)降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁带来的开销。

    2)提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立刻执行。

    3)提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗资源,而且会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

    但是,要充分发挥线程池的优点,就必须掌握其使用方法和适用条件,对其实现原理也必须了如指掌。下面我们主要介绍线程池的主要实现类ThreadPoolExecutor。

二、线程池的状态

    ThreadPoolExecutor中一共有5种状态。分别是:

  • RUNNING:接受新的任务并且处理阻塞队列中的任务。
  •  SHUTDOWN:拒绝新的任务,但是会将阻塞队列中的任务完成。
  • STOP:拒绝新的任务,抛弃阻塞队列中的任务,中断正在处理的任务。
  •  TIDYING:所有任务都已经执行完成,线程池中活动线程为0,阻塞队列也为空,此时将要调用 terminated()方法。
  •  TERMINATED:执行完terminated()方法,线程池处于终止状态。

三、线程池状态转换

  •    RUNNING=》SHUTDOWN

            显示调用shutdown()方法,或者隐式调用finalize(),它里面调用了shutdown()方法。


  • RUNNING or SHUTDOWN =》STOP

            显式调用shutdownNow()方法。


  • SHUTDOWN =》 TIDYING

            当线程池和阻塞队列都为空时。


  • STOP=》TIDYING

            当线程池为空是。


  • TIDYING=》TERMINATED

            当terminated()的hook方法执行完成时。


四、线程池参数

  • corePoolSize:线程池核心线程个数
  • workQueue:阻塞队列,用于保存等待执行的任务。
  • maximumPoolSize:线程池最大线程数量
  • threadFactory:创建线程的工厂
  • RejectedExecutionHandler:饱和策略,当队列满了并且活动线程数达到maximumPoolSize时采取的策略。
  • keepAliveTime:存活时间,当活动线程数大于核心线程数并且处于闲置状态,这些闲置线程能存活的最大时间。
  • TimeUnit:存活时间单位。

五、线程池主要处理流程


    当向线程池提交一个任务之后,线程池的主要处理流程如图:




java大量线程不释放 java线程释放资源_java大量线程不释放


  1. 判断核心线程池里面线程是否已满。如果未满,则创建新的工作线程执行任务。如果已满,则进入下个流程。
  2. 判断工作队列是否满了。如果未满,则加入工作队列。如果满了,进入下个流程。
  3. 判断线程池线程数有没有达到最大线程数。如果满了,采取饱和策略。如果未满,创建线程执行任务。

    之所以采取上述的设计思路,是因为1、3创建新线程需要获取全局锁,开销很大。在ThreadPoolExecutor预热之后,绝大部分execute()方法调用都是执行第二步,而这时不需要全局锁的。


六、RejectedExecutionHandler策略


    当队列和线程池都满了,必须采取一种策略处理提交的新任务。默认是AbortPolicy。java 1.5中提供了四种策略:


  • AbortPolicy:直接抛出异常。
  • CallerRunsPolicy:只用调用者线程来运行。
  • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  • DiscardPolicy:不处理,直接丢弃。