线程池你真不来了解一下吗?

2018 年 5 月 12 日 开源中国 Java3y


文章链接:https://my.oschina.net/u/3777556/blog/1807447

作者:Java3y

首发公众号:Java3y


作者语:本篇主要是讲解线程池,这是我在多线程的倒数第二篇了,后面还会有一篇死锁。主要将多线程的基础过一遍,以后有机会再继续深入!


那么接下来就开始吧,如果文章有错误的地方请大家多多包涵,不吝在评论区指正哦~


声明:本文使用JDK1.8


一、线程池简介


线程池可以看做是线程的集合。在没有任务时线程处于空闲状态,当请求到来:线程池给这个请求分配一个空闲的线程,任务完成后回到线程池中等待下次任务**(而不是销毁)。这样就实现了线程的重用**。


我们来看看如果没有使用线程池的情况是这样的:


  • 为每个请求都新开一个线程!



为每个请求都开一个新的线程虽然理论上是可以的,但是会有缺点:


  • 线程生命周期的开销非常高。每个线程都有自己的生命周期,创建和销毁线程所花费的时间和资源可能比处理客户端的任务花费的时间和资源更多,并且还会有某些空闲线程也会占用资源。

  • 程序的稳定性和健壮性会下降,每个请求开一个线程。如果受到了恶意攻击或者请求过多(内存不足),程序很容易就奔溃掉了。


所以说:我们的线程最好是交由线程池来管理,这样可以减少对线程生命周期的管理,一定程度上提高性能。


二、JDK提供的线程池API


JDK给我们提供了Excutor框架来使用线程池,它是线程池的基础。


  • Executor提供了一种将**“任务提交”与“任务执行”**分离开来的机制(解耦)


下面我们来看看JDK线程池的总体api架构:

 

 

接下来我们把这些API都过一遍看看:


Executor接口:

 

 

ExcutorService接口:

 

 

AbstractExecutorService类:

 

 

ScheduledExecutorService接口:

 

 

ThreadPoolExecutor类:

 

 

ScheduledThreadPoolExecutor类:

 

 

2.1 ForkJoinPool线程池


除了ScheduledThreadPoolExecutor和ThreadPoolExecutor类线程池以外,还有一个是JDK1.7新增的线程池:ForkJoinPool线程池


于是我们的类图就可以变得完整一些:

 

 

JDK1.7中新增的一个线程池,与ThreadPoolExecutor一样,同样继承了AbstractExecutorService。


ForkJoinPool是Fork/Join框架的两大核心类之一。与其它类型的ExecutorService相比,其主要的不同在于采用了工作窃取算法(work-stealing):所有池中线程会尝试找到并执行已被提交到池中的或由其他线程创建的任务。


这样很少有线程会处于空闲状态,非常高效。这使得能够有效地处理以下情景:大多数由任务产生大量子任务的情况;从外部客户端大量提交小任务到池中的情况。


来源:


  • blog.csdn.net/panweiwei19…


2.2 补充:Callable和Future


学到了线程池,我们可以很容易地发现:很多的API都有Callable和Future这么两个东西。



其实它们也不是什么高深的东西~~~


我们可以简单认为:Callable就是Runnable的扩展。


  • Runnable没有返回值,不能抛出受检查的异常,而Callable可以!

 

 

也就是说:当我们的任务需要返回值的时,我们就可以使用Callable!


Future一般我们认为是Callable的返回值,但他其实代表的是任务的生命周期(当然了,它是能获取得到Callable的返回值的)

 

 

简单来看一下他们的用法:



Callable任务:



执行完任务之后可以获取得到任务返回的数据:

 

 

三、ThreadPoolExecutor详解


这是用得最多的线程池,所以本文会重点讲解它。


我们来看看顶部注释:

 

 

3.1 内部状态

 

 

变量ctl定义为AtomicInteger,记录了“线程池中的任务数量”和“线程池的状态”两个信息。

 

 

线程的状态:


  • RUNNING:线程池能够接受新任务,以及对新添加的任务进行处理。

  • SHUTDOWN:线程池不可以接受新任务,但是可以对已添加的任务进行处理。

  • STOP:线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

  • TIDYING:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。

  • TERMINATED:线程池彻底终止的状态。

 

 

各个状态之间转换:

 

 


3.2 已默认实现的池


下面我就列举三个比较常见的实现池:


  • newFixedThreadPool

  • newCachedThreadPool

  • SingleThreadExecutor


如果读懂了上面对应的策略呀,线程数量这些,应该就不会太难看懂了。


3.2.1 newFixedThreadPool


一个固定线程数的线程池,它将返回一个corePoolSize和maximumPoolSize相等的线程池。



3.2.2 newCachedThreadPool


非常有弹性的线程池,对于新的任务,如果此时线程池里没有空闲线程,线程池会毫不犹豫的创建一条新的线程去处理这个任务。



3.2.3 SingleThreadExecutor


使用单个worker线程的Executor



3.3 构造方法


我们读完上面的默认实现池还有对应的属性,再回到构造方法看看


  • 构造方法可以让我们自定义(扩展)线程池



  1. 指定核心线程数量

  2. 指定最大线程数量

  3. 允许线程空闲时间

  4. 时间对象

  5. 阻塞队列

  6. 线程工厂

  7. 任务拒绝策略


再总结一遍这些参数的要点:


线程数量要点:


  • 如果运行线程的数量少于核心线程数量,则创建新的线程处理请求

  • 如果运行线程的数量大于核心线程数量,小于最大线程数量,则当队列满的时候才创建新的线程

  • 如果核心线程数量等于最大线程数量,那么将创建固定大小的连接池

  • 如果设置了最大线程数量为无穷,那么允许线程池适合任意的并发数量


线程空闲时间要点:


  • 当前线程数大于核心线程数,如果空闲时间已经超过了,那该线程会销毁。


排队策略要点:


  • 同步移交:不会放到队列中,而是等待线程执行它。如果当前线程没有执行,很可能会新开一个线程执行。

  • 无界限策略:如果核心线程都在工作,该线程会放到队列中。所以线程数不会超过核心线程数

  • 有界限策略:可以避免资源耗尽,但是一定程度上减低了吞吐量


当线程关闭或者线程数量满了和队列饱和了,就有拒绝任务的情况了:


拒绝任务策略:


  • 直接抛出异常

  • 使用调用者的线程来处理

  • 直接丢掉这个任务

  • 丢掉最老的任务


四、execute执行方法


execute执行方法分了三步,以注释的方式写在代码上了~


public void execute(Runnable command) {

    if (command == null)

        throw new NullPointerException();

    int c = ctl.get();

//如果线程池中运行的线程数量<corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。

    if (workerCountOf(c) < corePoolSize) {

        if (addWorker(command, true))

            return;

        c = ctl.get();

    }


//如果线程池中运行的线程数量>=corePoolSize,且线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,就再次检查线程池的状态,

// 1.如果线程池不是RUNNING状态,且成功从阻塞队列中删除任务,则该任务由当前 RejectedExecutionHandler 处理。

// 2.否则如果线程池中运行的线程数量为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。

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

    }

// 如果以上两种case不成立,即没能将任务成功放入阻塞队列中,且addWoker新建线程失败,则该任务由当前 RejectedExecutionHandler 处理。

    else if (!addWorker(command, false))

        reject(command);

}


五、线程池关闭


ThreadPoolExecutor提供了shutdown()和shutdownNow()两个方法来关闭线程池

shutdown() :


 

shutdownNow():

 

 

区别:


  • 调用shutdown()后,线程池状态立刻变为SHUTDOWN,而调用shutdownNow(),线程池状态立刻变为STOP。

  • shutdown()等待任务执行完才中断线程,而shutdownNow()不等任务执行完就中断了线程。


六、总结


本篇博文主要简单地将多线程的结构体系过了一篇,讲了最常用的ThreadPoolExecutor线程池是怎么使用的~~~


明天希望可以把死锁写出来,敬请期待~~~


还有剩下的几个线程池(给出了参考资料):


  • ScheduledThreadPoolExecutor

    • https://blog.csdn.net/panweiwei1994/article/details/78997029

    • http://cmsblogs.com/?p=2451

  • ForkJoinPool

    • https://blog.csdn.net/panweiwei1994/article/details/78992098


参考资料:


  • 《Java核心技术卷一》

  • 《Java并发编程实战》

  • http://cmsblogs.com/?page_id=111

  • https://blog.csdn.net/panweiwei1994/article/details/78483167

  • https://zhuanlan.zhihu.com/p/35382932




推荐阅读

写作 C# 读作 C 井,这些程序员常用词汇你读对了吗?

ARM 授权费用太贵科技巨头欲转向开源架构 RISC-V

微软发布 Windows 命令行参考文档

Spring Boot 应用监控实战

谷歌终于开放 ".app" 顶级域名注册

点击“阅读原文”查看更多精彩内容

登录查看更多
0

相关内容

应用程序接口(简称 API),又称为应用编程接口,就是软件系统不同组成部分衔接的约定。
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
算法与数据结构Python,369页pdf
专知会员服务
161+阅读 · 2020年3月4日
模型压缩究竟在做什么?我们真的需要模型压缩么?
专知会员服务
27+阅读 · 2020年1月16日
【书籍推荐】简洁的Python编程(Clean Python),附274页pdf
专知会员服务
179+阅读 · 2020年1月1日
【课程】伯克利2019全栈深度学习课程(附下载)
专知会员服务
56+阅读 · 2019年10月29日
文章太长不想看?ML 文本自动摘要了解一下
机器之心
8+阅读 · 2019年7月8日
深度学习了解一下(附53页Slides)
专知
48+阅读 · 2019年5月20日
Python用法速查网站
Python程序员
17+阅读 · 2018年12月16日
关于机器学习你要了解的 5 件事
机器学习算法与Python学习
7+阅读 · 2018年9月7日
为什么你应该学 Python ?
计算机与网络安全
4+阅读 · 2018年3月24日
从基础概念到实现,小白如何快速入门PyTorch
机器之心
13+阅读 · 2018年2月26日
TensorFlow神经网络教程
Python程序员
4+阅读 · 2017年12月4日
Pytorch还是Tensorflow?英伟达工程师帮你总结了
人工智能头条
5+阅读 · 2017年10月27日
Arxiv
101+阅读 · 2020年3月4日
Self-Attention Graph Pooling
Arxiv
13+阅读 · 2019年6月13日
Factor Graph Attention
Arxiv
6+阅读 · 2019年4月11日
Arxiv
27+阅读 · 2017年12月6日
VIP会员
相关VIP内容
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
算法与数据结构Python,369页pdf
专知会员服务
161+阅读 · 2020年3月4日
模型压缩究竟在做什么?我们真的需要模型压缩么?
专知会员服务
27+阅读 · 2020年1月16日
【书籍推荐】简洁的Python编程(Clean Python),附274页pdf
专知会员服务
179+阅读 · 2020年1月1日
【课程】伯克利2019全栈深度学习课程(附下载)
专知会员服务
56+阅读 · 2019年10月29日
相关资讯
文章太长不想看?ML 文本自动摘要了解一下
机器之心
8+阅读 · 2019年7月8日
深度学习了解一下(附53页Slides)
专知
48+阅读 · 2019年5月20日
Python用法速查网站
Python程序员
17+阅读 · 2018年12月16日
关于机器学习你要了解的 5 件事
机器学习算法与Python学习
7+阅读 · 2018年9月7日
为什么你应该学 Python ?
计算机与网络安全
4+阅读 · 2018年3月24日
从基础概念到实现,小白如何快速入门PyTorch
机器之心
13+阅读 · 2018年2月26日
TensorFlow神经网络教程
Python程序员
4+阅读 · 2017年12月4日
Pytorch还是Tensorflow?英伟达工程师帮你总结了
人工智能头条
5+阅读 · 2017年10月27日
Top
微信扫码咨询专知VIP会员