【线程池01】-线程池的概念以及常见参数

2020-04-09  本文已影响0人  qizhongju

title: 【线程池01】-线程池的概念以及常见参数
date: 2018-12-06 12:16:12
tags:


线程池作为「Java高并发多线程」里一个重要的知识点,在实际工作和面试过程中都有用到。
这里线程池第一篇将会整理一些线程池相关的概念,以及介绍线程池的各个参数。

什么是线程池(what)?

线程池是一种基于池化技术管理线程的方式,类似的还有很多:数据库连接池、Http连接池、对象池(内存池)。提前创建一批资源放到池子里,在使用的时候直接来进行资源的申请,节约了资源创建和释放的系统消耗,同时池子控制住大小,防止不断的资源申请导致系统奔溃。

参考美团技术推文:「线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。」

相比我的描述更加清晰准确些。

为什么要使用多线程(why)?

在传统的web开发中,服务器接收到请求后都需要一个线程来处理请求。
每次请求过来创建一个线程,使用起来也很方便,可是这样就会存在这样一个问题:

如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率。可能出现服务器在为每个请求创建新线程和销毁线程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多。

在这种情况下,线程池的存在就很有价值。线程池为线程生命周期的开销和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。

总结来说,线程池有如下优点:

如何使用线程池(how)?

Java中主要是使用J.U.C提供的ThreadPoolExecutor类来使用线程池,这里主要先总结整理下线程池的几个常用方法以及线程池的常用参数,后面再针对具体内部的代码和使用做整理。

创建线程池都可以通过Executors类来实现,此类主要提供了一系列工厂方法来创建线程池,返回的线程池都实现了ExrcutorService接口。

//创建一个固定大小的线程池,超出的部分在队列等待
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

//创建一个单线程的线程池
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程(60s),若无可回收,则新建线程。
public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

//创建一个单线程线程池,支持定时及周期性任务执行
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

//创建一个定长线程池,支持定时及周期性任务执行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)

//会创建一个含有足够多线程的线程池,来维持相应的并行级别
//它会通过工作窃取的方式,使得多核的 CPU 不会闲置,总会有活着的线程让 CPU 去运行
public static ExecutorService newWorkStealingPool(int parallelism)
public static ExecutorService newWorkStealingPool()

但是,阿里开发手册有如下一条规定:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式

这是因为 Executor.newCachedThreadPool() 和 Executors#newScheduledThreadPool() 两个方法最大线程数为Integer.MAX_VALUE,如果达到最大值,系统可能OOM。Executors.newSingleThreadExecutor() 和Executors.newFixedThreadPool() 两个方法的 workQueue 参数为new LinkedBlockingQueue<Runnable>(),容量为Integer.MAX_VALUE,如果队列非常大,也会有OOM风险。

所以一般情况下使用线程池使用ThreadPoolExecutor 提供的构造进行线程池创建,实际上Executors 提供的方法就是对此构造的封装。最底层的构造方法如下,下面将会依次介绍各个参数。

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit,
                              BlockingQueue<Runnable> workQueue, 
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

核心参数说明

线程池提供了四种策略:

具体的线程池使用示例会在下一篇中总结。

上一篇 下一篇

猜你喜欢

热点阅读