Java并发编程的艺术(六)——线程池

我们为什么要使用线程池?

顾名思义,线程池就是一个存放了很多线程的“池”,就像粮仓一样,每次我们需要使用线程的时候都可以先去“池子”里看看有没有空闲的线程,如果有直接拿出来用就可以了。

为什么要提前准备好这些线程并把他们存储起来呢?

因为对于系统来说创建和销毁线程是非常消耗资源的,如果我们提前把一定数量的线程创建好并存储起来(不进行销毁),那么系统一旦到需要使用线程的时候就不需要重新创建了,而是到存储线程的地方取出一个线程资源进行使用,用完了再把该资源归还存储的地方,从而避免了不断创建销毁线程的过程,节约了大量的系统资源。

所以在需要异步或者并发执行任务的程序中都可以使用线程池来提高执行效率,但是在使用线程池的时候需要对实际业务进行评估,合理的设置线程池中线程的数量才能达到事半功倍的效果。

一个线程池可以说被划分为4个部分,分别是核心线程池,线程池,任务队列,以及异常处理。具体可以参考以下线程池的结构组成与主要处理过程:
在这里插入图片描述

上图中的1,2,3,4序号为线程池主要处理过程顺序,其处理逻辑顺序下图所示(线程池处理逻辑图)

在这里插入图片描述

有了一定的理论知识,我们就可以深入到编程语言中看看如何使用线程池。

Java中线程池的创建是如何实现的呢?

在Java中通常使用ThreadPoolExecutor 来创建新的线程池
在这里插入图片描述

线程池常用参数解释:

corePoolSize: 核心线程池中线程数量
maximumPoolSize: 线程池中能容纳的最大线程数量
keepAliveTime: 线程存活时间
TimeUnit: 存活时间单位
workQueue: 该队列用来暂时存储未被执行的线程

我们可以根据业务的实际需求来设置线程数量,以及采用何种队列作为存储队列。这里就涉及到如何比较准确的设置线程池的线程数量。

如何设置线程池大小?

系统的性质一般可分为:CPU密集型、IO密集型与混合型,对于不同类型的任务需要具体问题具体分析,设置不同大小的线程池。

CPU密集型任务

尽量使用较小的线程池,一般为CPU核心数+1。
因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。

IO密集型任务

可以使用稍大的线程池,一般为2*CPU核心数。
IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。

混合型任务

可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。
只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。
因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,反而没有好的结果。

所以在开发的时候不能盲目的使用技术,要结合具体情况具体的需求进行分析,选择合适的技术,再好的武器用的恰当才能发挥出最大的能量:)

Java并发编程模块的内容到这里就告一段落了,回看过去写的收获满满,后期如果有补充的知识点我会另外完善,微信公众号和本人的CSDN博客同步更新,欢迎小伙伴们提出建设性意见。接下来想撸一手Spring或者Mybatis的知识点了

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章