什么是线程池,如何使用,为什么要用

线程池是使用线程的模型。
线程池维护多个线程,等待supervisor分配可以并发执行的任务。

作用:应用程序可以有多个线程,这些线程必须花费大量时间处于休眠状态等待事件发生。
其他线程可能会进入休眠状态,并且仅在返回休眠之前定期被唤醒以循环修改或状态更新信息。

为了简化这些线程的管理,.NET框架为每个进程提供了一个线程池。
线程池有多个挂起操作状态。
当挂起的操作完成时,辅助线程池。
线程将执行回调函数。
线程池中的线程由系统管理。
程序员无需担心线程管理,可以集中精力处理应用程序任务。

扩展信息:

应用范围

1需要大量线程来完成任务,所以比时间必要的。
完成任务的时间比较短。
当WEB服务器执行网页请求等任务时,使用线程池技术是非常合适的。
因为单个任务很小,任务数量巨大,你可以想象一个热门网站的点击量。
但对于长时间运行的任务(例如Telnet连接请求),线程池的好处并不明显。
因为Telnet会话持续时间比线程创建持续时间长得多。

2.对性能要求较高的应用程序,例如要求服务器快速响应客户端请求。

3.接受突然大量请求的应用程序,而服务器不会产生大量线程。
突然大量的客户端请求会在没有线程池的情况下产生大量线程。
尽管理论上最大线程数在大多数操作系统中不是问题,但可能会导致在短时间内产生大量线程。
内存达到极限,会出现“OutOfMemory”错误。

参考来源:百度百科-线程池

超详细的线程池使用解析

Java中的线程池是适合大多数应用场景的并发框架。
几乎所有需要异步或并发执行任务的程序都可以使用线程池。
正确使用线程池可以带来几个好处:

(1)减少资源消耗。
通过重用制造的线程来降低线程创建和销毁的成本。

(2)提高响应速度。
在处理执行任务时,任务可以立即执行,无需等待线程创建。

(3)提高线程的可管理性。
线程是一种稀缺资源。
如果无限制地创建它们,不仅会消耗系统资源,还会降低系统稳定性。
线程池可用于统一分配、调优和监控。

线程池的处理流程如上图所示。

线程池的当前状态由线程池中的ctl字段表示,类型为ctl。
AtomicInteger,它包裹了两个概念字段:workerCount和runState,workerCount表示有效线程数,runState表示是否正在运行、停止等。
使用ctl字段来表示这两个概念。
ctl的前3位表示线程池状态。
线程池中的workercount限制为(2^29)-1(约5亿)个线程,而不是(2^31)。
-1(200亿)个线程。
WorkerCount是允许启动且不允许停止的worker数量。
该值可能暂时与实际动线程数不同,例如当ThreadFactory在被请求时无法创建线程,甚至在线程终止之前执行退出时也无法创建线程。
用户可见的池大小报告为工作集的当前大小。
RunState提供了主要的生命周期控制,其值如下表所示:

RunState随着时间的推移而变化,在waitFinish()方法中等待的线程随后会返回,当王国结束时。

状态更改如下:

Shutdown()时,关闭可能隐含在最终确定中

Decluttering队列和。
进程池为空时队列

清理

terminate()方法完成时终止

开发者如果需要在线程池变为TIDYING状态时进行相关处理,可以通过重载terminate()函数来实现。

结合上图来说明线程池ThreadPoolExecutor执行流程,使用execute()方法提交任务到线程池执行时有四种场景:

(1)线程池中运行的线程数量小于corePoolSize,创建新的线程来执行任务。

(2)线程池中运行的线程数不小于corePoolSize,将任务添加到阻塞队列中。

(3)如果任务无法添加到阻塞队列(队列已满),则创建一个新线程来处理该任务(这里需要获取全局锁)。

(4)当如果由于创建的新线程数量导致线程池中当前运行的线程数量超过MaximumPoolSize,则该任务将从线程池中拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

源码分析:

线程池创建线程时,会将线程封装成工作线程worker,工作线程完成任务后,会实现for循环。
也会将任务放入任务队列中执行。

在创建线程池之前,需要了解创建线程池的主要参数:

corePoolSize(核心线程数):向线程池提交任务时,线程池将会被创建。
一个线程用来执行任务,即使其他空闲的基本线程可以执行新的任务,也会创建线程,直到要执行的任务数量超过核心线程数量。

RunnableTaskQueue(任务队列):阻塞队列,用于保存等待执行的任务。
一般选择以下几种:

ArrayBlockingQueue:基于数组的有界阻塞队列,按照先进先出原则对元素进行排序。

LinkedBlockingQueue:基于链表的阻塞队列,按照先进先出原则对元素进行排序。

同步队列:同步阻塞队列,也是不存储元素的阻塞队列。
每个插入操作必须等待,直到另一个线程调用删除操作,否则插入操作将阻塞。

PriorityBlockingQueue:PriorityBlockingQueue,一个具有优先级的无限阻塞队列。

最大池Size(最大线程数小):线程池可以创建的最大线程数。
当队列已满并且线程池中的线程数低于最大线程数时,线程池将创建新线程以提高性能。
工作。
当使用无限队列时,此参数没有用处。

RejectedExecutionHandler(拒绝策略):当任务队列和线程池都满了,这意味着线程池处于饱和状态时,拒绝策略用于处理新提交的任务。
JDK内置了4种拒绝策略:

AbortPolicy:直接抛出异常

CallerRunsPolicy:使用调用者的线程执行任务

DiscardOldestPolIcy:丢弃队列中最新的任务执行当前任务

丢弃策略:不处理直接丢弃

您可以根据应用场景实现RejectedExecutionHandler接口的自定义处理策略。
可以做。

keepAliveTime(线程生存时间):线程池中的工作线程空闲后保持存活的时间。

时间单位(生存时间的单位):可供选择的单位有日(day)、小时(hour)、分钟(分钟)、毫秒(millisecond)、微秒(microsecond)、纳秒(nanosecond)。

ThreadFactory:可以使用ThreadFactory为创建的线程设置一个有意义的名称。

创建线程池主要分为两类:第一类是通过Executor工厂类创建线程池,第二类是创建自定义线程池。
遵循《阿里巴巴Java开发手册》规范,避免资源耗尽的风险不允许使用执行器创建线程池。

创建单线程线程池

创建固定线程数的线程池

以上两种创建线程池的方式都使用了链接list.做阻塞队列来存储任务,实际场景中可能会积累大量请求导致OOM

创建可缓存的线程池

允许创建的最大线程数整数.MAX_VALUE。
当创建大量线程时,CPU负载会很重,会出现OOM,对此可以采用两种方法。
向线程池提交任务,即execute()和submit()。

execute()方法用于提交不需要返回值的任务,因此无论执行与否都无法判断该任务是否被线程池执行成功。
传递给execute()方法的是Runnable类的实例。

submit()方法用于提交需要返回值的函数。
线程池将返回一个Future类型的对象。
Future对象可以用来判断任务是否执行成功,返回值可以通过Future的get()方法获取。
get()方法将阻塞当前线程,直到任务完成。
使用get(longtimeout,TimeUnitunit)方法会阻塞当前线程一段时间并立即返回。

通过调用线程池的Shutdown()或ShutdownNow()方法来创建线程池。
可以关闭。
它们的原理就是将工作线程传入线程池,然后一一调用interrupt()方法来中断线程,这样无法响应中断的任务就永远无法终止。

shutdown()和shutdownNow()方法的区别在于shutdownNow方法首先将线程池的状态设置为停止,然后尝试停止正在执行任务或挂起的线程,并返回等待执行的任务列表,而shutdown只是将线程池的状态设置为关闭状态,然后中断所有未执行任务的线程。

使用线程池时的主要问题是线程池的参数不容易配置。
一方面,线程池的运行机制不太了解,正确的配置需要强烈依赖开发人员的个人经验和知识;另一方面,线程池的执行与任务类型密切相关;,IO密集型和CPU密集型。
任务运行的环境千差万别,业界并没有成熟的体验策略来帮助开发者进行情境化。

(1)根据任务类型简单评估:

假设设置了线程池的大小(N为CPU个数)

如果是pure在计算任务中,多线程并不能提高性能,因为CPU处理能力是稀缺资源,反而增加了线程切换成本,建议多线程;设该数字为CPU数量或+1;----为什么+1?因为它可以防止N个线程中的任何一个被中断或意外退出,所以CPU不会等待CPU空闲。

如果是IO密集型应用,进程池大小设置为2N+1。
线程数=CPU核心数、目标CPU利用率(1+平均等待时间/平均任务时间)

(2)任务数基于理想情况评估:

1)默认值

2)如何设置*需要根据相关值来决定-taSKS:每秒任务数,假设为500~1000-Taskcost:每个任务花费的时间上,0.1考虑的秒数——响应时间:系统允许的最大响应时间,考虑1秒

以上所有理想值都是根据机器在实际情况下的性能来确定的。
如果在达到大线程数之前机器CPU负载就满了,需要通过升级硬件、优化代码、降低任务成本来处理。

(这只是对理想情况的简单评估,可以作为线程池参数设置的参考)

与主业务没有直接数据的从业务否依赖项可以使用异步线程池进行处理,项目启动时创建线程池,将业务中的任务提交到异步线程池执行可以减少响应时间。

当任务需要按照指定的顺序(先进先出、后进先出、优先级)执行时,单线程线程池建议制作。

本文主要讲解线程池的执行原理和构建方法,以及推荐的线程池参数设置和常见使用场景。
开发过程中,开发人员需要根据业务合理创建和使用线程池,以减少资源消耗,提高响应速度。