线程池

一. 好处

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

二. 线程池的使用

java.util.concurrent.ThreadPoolExecutor类.

2.1 创建线程池

创建一个线程池有以下4个构造函数, 其中前3个都最终调用了最后一个.

image

image

2.2 线程池的主要工作机制

  1. 提交新任务到线程池
  2. 若核心池未满,创建一个工作线程来执行任务. 若已满,下一步.
  3. 若工作队列未满, 新提交的任务入队. 若已满,下一步.
  4. 若线程池未满,创建一个工作线程来执行任务. 若已满,交给饱和策略来处理.

image

image

2.3 线程池中线程的状态

image

  • running, 调用了shutdown()后变为shutdown. 当队列为空且线程池为空, tidying态
  • running或shutdown, 调用了shutdownNow()后变为stop. 当线程池为空时, tidying态.
  • tidying, 调用terminated()后变为terminated.

三. 合理配置线程池

3.1 任务的性质

CPU密集型任务, 尽可能少的线程. 如 Ncpu+1个线程.

IO密集型任务, 由于线程不是一直在执行任务, 可配置尽可能多的线程. 如 2*Ncpu

混合型任务,如果可以拆分为两个执行时间相差不太大的CPU密集型任务和IO密集型任务,分解后的吞吐率要高于串行的吞吐率。如果执行时间相差太大,就没必要分解。

3.2 优先级

PriorityBlockingQueue. 优先级高的任务先执行。 但如果一直有高优先级的任务加入,低优先级的任务可能永远无法执行。

3.3 执行时间

执行时间不同的线程,可以交给不同规模的线程池,也可以使用优先队列,优先执行时间较短的线程。

3.4 数据库连接

依赖数据库连接的任务,因为线程提交SQL之后需要等待数据库返回结果,等待时间越长,CPU空闲时间越多。所以线程数应该设置大一些,才能更好地利用CPU。

3.5 有界队列

建议使用有界队列。有界队列能增加系统的稳定性和预警能力。可以根据需要设置大一些,比如几千。

如果后台线程池里的线程全部要向数据库查询和插入数据,一旦数据库出了问题,导致SQL很慢,线程阻塞,就会大量积压。如果这时设置为无界队列,就有可能撑满内存,导致整个系统不可用。

四、线程池的监控

  • taskCount:线程池需要执行的任务数量。
  • completedTaskCount:线程池在运行过程中已完成的任务数量。小于或等于taskCount。
  • largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
  • getPoolSize:线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不+ getActiveCount:获取活动的线程数。

通过扩展线程池进行监控。通过继承线程池并重写线程池的beforeExecute,afterExecute和terminated方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情。如监控任务的平均执行时间,最大执行时间和最小执行时间等。这几个方法在线程池里是空方法。

 

 

参考资料

http://www.infoq.com/cn/articles/java-threadPool

http://blog.csdn.net/mazhimazh/article/details/19291965

posted @ 2016-08-11 00:21  流年素心  阅读(245)  评论(0编辑  收藏  举报