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


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

  • 多线程
  • 线程池
  • Executor
    categories: Java高并发多线程

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

什么是线程池(what)?

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

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

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

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

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

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

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

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

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。

  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。

  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。

  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

如何使用线程池(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) 

核心参数说明

  • corePoolSize
    核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。
  • maxPoolSize
    最大线程数量,当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。 |
  • keepAliveTime
    线程最大存活时间,超过核心线程数的线程,如果在此参数指定的时间内还没有接收到新任务,则会被销毁。也就是说此参数是指定超过核心线程数的空闲线程的最大存活时间 |
  • workQueue
    等待任务队列 ,用来保存通过execute(Runnable command)方法提交的未执行的任务。也就是在任务执行之前用来保存任务的队列。 |
  • threadFactory
    线程工厂,生成线程的工厂类
  • handler
    线程池的饱和策略,如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务

线程池提供了四种策略:

  • AbortPolicy : 直接抛出RejectedExecutionException异常 这是默认策略;
  • CallerRunsPolicy:用调用者所在的线程来执行任务;
  • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
  • DiscardPolicy:直接丢弃任务;

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容