java线程池分类图解(一文搞懂JAVA线程池工作原理)

线程池详解

带着问题看源码:

1. 使用线程池有什么好处和作用?

2. 怎么创建线程池?

3. 线程池原理?

4. 线程池是如何复用线程的?

5. 线程池如何合理的配置?

线程是系统稀缺资源,如果不加限制的创建,不仅会消耗系统资源,严重的还会影响系统性能。所以希望有个统一的地方统一对线程的分配,调优和监控,这就是线程池。

使用线程池带来的好处:

1. 降低消耗,重复利用已创建好的线程,降低线程创建和销毁造成的消耗;

2. 提高响应速度,当任务到达时,任务可以不用创建线程就可以执行;

3. 提高线程的可管理性。

作用:为突然大量暴发的执行时间短的线程设计的,用几个固定的复用线程去为大量的操作服务。而不用大量频繁地创建销毁线程而浪费系统资源。

线程池的创建

JDK提供了Executors工具类,里面封装好了四种线程池的创建。

newCachedThreadPool:创建一个可根据需要创建线程的缓存线程池,可有线程可用时将重新使用以前的线程,否则将新一个线程并添加到线程池里。当线程空闲时间60S则终止并从缓存里删除。这样即使长时间保持空间也不会浪费资源。

newFixedThreadPool :创建一个固定长度的线程池。可控制最大的并发量,超出的线程则在队列里等待。

newScheduledThreadPool :创建一个定时任务线程池,可在给定的延迟时间或定期执行。

newSingleThreadExecutor:创建一个单线程的线程池,用唯一的一个线程来执行,可保证任务按顺序执行。

原理分析

线程池里的几个核心概念:

1. corePoolSize:核心线程数

2. maximumPoolSize:线程池最大线程数

3. keepAliveTime:线程没有执行任务的存活时间

4. workQueue:工作队列(阻塞队列BlockingQueue)

当一个任务到达时,线程池处理流程:

a) 判断工作线程数 < 核心线程数,如果小于则新建一个线程来执行任务;否则进入下一判断;

b) 工作队列未满时,则将任务加入队列;

c) 如果工作队列满了,判断工作线程数是否小于线程池的最大线程数,小于则新建一个线程来直接执行任务,大于则交由拒绝策略来执行此任务。

java线程池分类图解(一文搞懂JAVA线程池工作原理)(1)

线程池处理流程图

当向线程池添加执行任务时,调用ThreadPoolExecutor.execute(Runnable command)方法

java线程池分类图解(一文搞懂JAVA线程池工作原理)(2)

核心方法:addWorker(Runnable command,Boolean core)方法功能就是新建一个任务并执行或加入到队列里。

java线程池分类图解(一文搞懂JAVA线程池工作原理)(3)

Worker是ThreadPoolExecutor的一个内部类,对执行任务的封装。

java线程池分类图解(一文搞懂JAVA线程池工作原理)(4)

构造函数里将当前要执行的任务设给自己的变量。

线程池运行这个线程的时候其实就是执行Worker的run()方法

java线程池分类图解(一文搞懂JAVA线程池工作原理)(5)

在runWorker()方法里对真正要执行的任务调用。

线程池复用线程也在这个方法里, runWorker ()里有很重要的一句话:

while (task != null || (task = getTask()) != null){}

task = getTask();当没有指定执行任务时,从工作队列里获取任务,在当前线程里循环执行完所有任务,这样就达到了复用线程的目的。

那什么时候worker里的任务会为空呢?

Execute()方法里

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);}

workerCountOf(recheck) == 0时会入addWorker()加入一个空的任务。

线程池配置

任何的架构或资源分配都需根据业务需求,想合理的配置线程池就必须分析任务特性,可以从以下几个角度分析:

1. 任务性质:CPU密集型任务,计算密集型任务,I/O密集型任务和混合型任务。

2. 任务的优先级:高中低。

3. 任务的执行时间:长中短。

4. 任务的依赖性:是否依赖系统的其他资源,如数据库连接。

任务性质不同就可以用不同规模的线程池来处理,CPU密集型任务需配置尽可能少的线程数,如CPU核数 1。这种类型的任务如果开太多线程处理会影响系统性能。

对于计算密集型的任务,在有N 个处理器的系统上,当线程池的大小为 N 1 时,能实现 CPU 的最优利用率。(即使当计算密集型的线程 偶尔由于页缺失故障或者其他原因暂停时,这个"额外" 的线程也能确保CPU 的时装周期不会被浪费。)对于 I/O 操作或其他 阻塞任务,由于线程并不会一直执行,因此线程池的规模应该更大。

一般计算公式:

最佳线程数目 = ((线程等待时间 线程CPU时间)/线程CPU时间 )* CPU数目

最佳线程数目 = (线程等待时间与线程CPU时间之比 1)* CPU数目

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页