为什么线程池里的方法会执行两次_新手一看就懂的线程池

作者:码农田小齐

来源:https://www.cnblogs.com/nycsde/p/14003888.html

那相信大家也能感受到,其实用多线程是很麻烦的,包括线程的创建、销毁和调度等等,而且我们平时工作时好像也并没有这样来 new 一个线程,其实是因为很多框架的底层都用到了线程池。

线程池是帮助我们管理线程的工具,它维护了多个线程,可以降低资源的消耗,提高系统的性能。

并且通过使用线程池,我们开发人员可以更好的把精力放在任务代码上,而不去管线程是如何执行的,实现任务提交和执行的解藕。

本文将从是何、为何、如何的角度来讲解线程池:

  1. 线程池是什么
  2. 为什么要用线程池
  3. 怎么用线程池

线程池 Thread Pool

线程池是一种池化的技术,类似的还有数据库连接池、HTTP 连接池等等。

池化的思想主要是为了减少每次获取和结束资源的消耗,提高对资源的利用率。

比如在一些偏远地区打水不方便的,大家会每段时间把水打过来存在池子里,这样平时用的时候就直接来取就好了。

线程池同理,正是因为每次创建、销毁线程需要占用太多系统资源,所以我们建这么一个池子来统一管理线程。用的时候从池子里拿,不用了就放回来,也不用你销毁,是不是方便了很多?

Java 中的线程池是由 juc 即 java.util.concurrent 包来实现的,最主要的就是 ThreadPoolExecutor 这个类。具体怎么用我们下文再说。

线程池的好处

在多线程的第一篇文章中我们说过,进程会申请资源,拿来给线程用,所以线程是很占用系统资源的,那么我们用线程池来统一管理线程就能够很好的解决这种 资源管理问题

比如因为不需要创建、销毁线程,每次需要用的时候我就去拿,用完了之后再放回去,所以节省了很多资源开销,可以提高系统的运行速度。

而统一的管理和调度,可以合理分配内部资源,根据系统的当前情况调整线程的数量。

那总结来说有以下 3 个好处:

  1. 降低资源消耗:通过重复利用现有的线程来执行任务,避免多次创建和销毁线程。
  2. 提高相应速度:因为省去了创建线程这个步骤,所以在拿到任务时,可以立刻开始执行。
  3. 提供附加功能:线程池的可拓展性使得我们可以自己加入新的功能,比如说定时、延时来执行某些线程。

说了这么多,终于到了今天的重点,我们来看下究竟怎么用线程池吧~

线程池的实现

Java 给我们提供了 Executor 接口来使用线程池。

947f79ad881730ef92e965a46684553f.png

我们常用的线程池有这两大类:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

它俩的区别呢,就是第一个是普通的,第二个是可以定时执行的。

当然还有其他线程池,比如 JDK 1.7 才出现的 ForkJoinPool ,可以把大任务分割成小任务来执行,最后再大一统。

那么任务提交到一个线程池之后,它会经历一个怎样的过程呢?

执行过程

线程池在内部实际上采用了生产者消费者模型(还不清楚这个模型的在文章开头有改文章的链接)将线程和任务解藕,从而使线程池同时管理任务和线程。

当任务提交到线程池里之后,需要经过以下流程:

608fade338bae5c039b810f047bc07f0.png
  1. 首先它检查 核心线程池 是否已满。这个核心线程池,就是不管用户量多少线程池始终维护的线程的池子。比如说线程池的总容量最多能装 100 个线程,核心线程池我们设置为 50,那么就无论用户量有多少,都保持 50 个线程活着。这个数字当然是根据具体的业务需求来决定的。
  2. 阻塞队列,就是 BlockingQueue ,在生产者消费者这节里提到过。
  3. 最后判断线程池是否已满,就是判断是不是已经有 100 个线程了,而不是 50 个。
  4. 如果已经满了,所以不能继续创建线程了,就需要按照 饱和策略 或者叫做 拒绝策略 来处理。这个饱和策略我们下文再讲。

ThreadPoolExecutor

我们主要说下 ThreadPoolExecutor ,它是最常用的线程池。

153885eae6bc66e49ce0634cf0f9f01e.png

这里我们可以看到,这个类里有 4 个构造方法,点进去仔细看,其实前三个都 call 了最后一个,所以我们只需要看最后一个就好。

public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue workQueue,                          ThreadFactory threadFactory,                          RejectedExecutionHandler handler) {    ...}

这里我们来仔细看下这几个参数:

  1. corePoolSize :这个就是上文提到过的 核心线程池 的大小,在核心里的线程是永远不会失业的。

corePoolSizethe number of threads to keep in the pool, even if they are idle, unless { @code allowCoreThreadTimeOut} is set

  1. maximumPoolSize :线程池的最大容量。

maximumPoolSize the maximum number of threads to allow in the pool

  1. keepAliveTime :存活时间。这个时间指的是,当线程池中的线程数量大于核心线程数,这些线程闲着之后,多久销毁它们。

keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.

  1. unit :对应上面存活时间的时间单位。

unitthe time unit for the { @code keepAliveTime} argument

  1. workQueue :这是一个阻塞队列,其实线程池也是生产者消费者模型的一种,任务 - 相当于生产者,线程 - 相当于消费者,所以这个阻塞队列是用来协调生产和消费的进度的。

workQueue the queue to use for holding tasks before they are executed.

  1. threadFactory :这里用到了工程模式,用来创建线程的。

threadFactory the factory to use when the executor creates a new thread

  1. handler :这个就是拒绝策略。

handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached

所以我们可以通过自己传入这 7 个参数构造线程池,当然了,贴心的 Java 也给我们包装好了几类线程池可以很方便的拿来使用。

  • newCachedThreadPool
  • newFixedThreadPool
  • newSingleThreadExecutor

我们具体来看每个的含义和用法。

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                  60L, TimeUnit.SECONDS,                                  new SynchronousQueue());}

这里我们可以看到,

Integer.MAX_VALUE

它的适用场景在源码里有说:

These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks.

来看怎么用:

public class newCacheThreadPool {    public static void main(String[] args) {        // 创建一个线程池        ExecutorService executorService = Executors.newCachedThreadPool();        // 向线程池提交任务        for (int i = 0; i < 50; i++) {            executorService.execute(new Task());//线程池执行任务        }        executorService.shutdown();    }}

执行结果:

e0ad51cd68ad3c32e0ce65dc34271b56.png

可以很清楚的看到,线程 1、2、3、5、6 都很快重用了。

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue());}

这个线程池的特点是:

  1. 线程池中的线程数量是固定的,也是我们创建线程池时需要穿入的参数;
  2. 超出这个数量的线程就需要在队列中等待。

它的适用场景是:

Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.
public class FixedThreadPool {    public static void main(String[] args) {        ExecutorService executorService = Executors.newFixedThreadPool(10);        for (int i = 0; i < 200; i++) {            executorService.execute(new Task());        }        executorService.shutdown();    }}
d3aa3ad3614451cc423aeeb1eeb61bd2.png

这里我限制了线程池里最多有 10 个线程,哪怕有 200 个任务需要执行,也只有 1-10 这 10 个线程可以运行。

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,                                0L, TimeUnit.MILLISECONDS,                                new LinkedBlockingQueue()));}

这个线程池顾名思义,里面只有 1 个线程。

适用场景是:

Creates an Executor that uses a single worker thread operating off an unbounded queue.

我们来看下效果。

public class SingleThreadPool {    public static void main(String[] args) {        ExecutorService executorService = Executors.newSingleThreadExecutor();        for (int i = 0; i < 100; i++) {            executorService.execute(new Task());        }        executorService.shutdown();    }}
a9ad6d9fd6aa5a1addb99861d8ee2df0.png

这里在出结果的时候我能够明显的感觉到有些卡顿,这在前两个例子里是没有的,毕竟这里只有一个线程在运行嘛。

小结

所以在使用线程池时,其实都是调用的 ThreadPoolExecutor 这个类,只不过传递的不同参数。

这里要特别注意两个参数:

workQueuehandler

那我们发现,在上面的 3 个具体线程池里,其实都没有设定 handler ,这是因为它们都使用了 defaultHandler 。

/** * The default rejected execution handler */private static final RejectedExecutionHandler defaultHandler =    new AbortPolicy();

在 ThreadPoolExecutor 里有 4 种拒绝策略,这 4 种策略都是 implements 了 RejectedExecutionHandler :

  1. AbortPolicy 表示拒绝任务并抛出一个异常 RejectedExecutionException 。这个我称之为“正式拒绝”,比如你面完了最后一轮面试,最终接到 HR 的拒信。
  2. DiscardPolicy 拒绝任务但不吭声。这个就是“默拒”,比如大部分公司拒简历的时候都是默拒。
  3. DiscardOldestPolicy 顾名思义,就是把老的任务丢掉,执行新任务。
  4. CallerRunsPolicy 直接调用线程处理该任务,就是 VIP 嘛。

所以这 3 种线程池都使用的默认策略也就是第一种,光明正大的拒绝。

好了以上就是本文的所有内容了。当然线程池还有很多知识点,比如 execute() 和 submit() 方法,线程池的生命周期等等。

但随着阅读量的逐渐走低,齐姐意识到了这似乎有什么误会,所以这篇文章是多线程系列的最后一篇了。

本文已收录至我的 Github 上: https://github.com/xiaoqi6666/NYCSDE

作者:码农田小齐

来源:https://www.cnblogs.com/nycsde/p/14003888.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556322.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue学习笔记(超详细)

文章目录一. Vue基础认识Vue.jsVue安装方式Vue的MVVM二. Vue基础语法生命周期模板语法创建Vue, options可以放什么语法综合v-onv-for遍历数组v-model表单绑定v-model结合radio类型使用v-model结合checkbox类型使用v-model结合select类型使用值绑定修饰符检测数组更新计算属性 c…

华为鸿蒙热水器,美的华为跨界联合!搭载鸿蒙OS的美的产品双11上市

【宅秘新闻】在前段时间的华为开发者大会上&#xff0c;华为正式发布了鸿蒙OS 2.0。华为消费者业务软件部总裁王成录当时表示&#xff0c;美的等家电品牌很快就会推出基于鸿蒙OS 2.0的家电产品。11月11日&#xff0c;美的集团官方宣布搭载鸿蒙OS的美的产品在双十一惊喜上市。宅…

跳一跳python_微信跳一跳python怎么刷分?跳一跳python怎么使用?

最近&#xff0c;微信跳一跳小游戏可以说是火爆了微信好友圈&#xff0c;网上流传了Github大神分享的一个针对该游戏的开源项目&#xff0c;也就是一个Python脚本程序可以刷很高的分&#xff0c;下面就为大家带来跳一跳python使用教程。微信跳一跳python怎么刷分&#xff1f;微…

Eclipse构建Maven分包分模块项目并构建服务端

首先说一下Maven 模块结构&#xff1a; 一个简单的Maven模块结构是这样的&#xff1a; ---- app-parent 一个父项目(app-parent)聚合很多子项目(app-util,app-dao,app-service,app-web) |---- pom.xml (pom) | |-------- app-util | |-------- pom.xml (jar) | |-------- app-…

我们一起动手学大模型应用开发

大模型正逐步成为信息世界的新革命力量&#xff0c;其通过强大的自然语言理解、自然语言生成能力&#xff0c;为开发者提供了新的、更强大的应用开发选择。 随着国内外井喷式的大模型 API 服务开放&#xff0c;如何基于大模型 API 快速、便捷地开发具备更强能力、集成大模型的…

登陆页面html5代码qq,HTML5QQ登录的一篇代码分享

html5背景*{margin:0;padding:0;list-style-type:none;}a,img{border:0;}$(function () {if (!window.ActiveXObject && !!document.createElement("canvas").getContext) {$.getScript("http://im-img.qq.com/pcqq/js/200/cav.js?_1428576021379"…

unity 多台 显示器 控制_飞利浦292E2E评测丨宽屏显示器中的多面手

■本文来自中关村在线屏幕比例21:9或32:9的显示器&#xff0c;因为横向屏幕很长&#xff0c;所以被人形象的称为带鱼屏。相较于16:9的显示器&#xff0c;屏幕拥有更多显示面积&#xff0c;我们能获得更多信息&#xff0c;大大提升办公效率。同时带鱼屏打游戏和看电影时也能获得…

html 表格行排序,用客户端HTML表格排序对行进行分组

可用于逻辑上(不一定是语义上)可用的唯一元素是将相关的行分组在一起的是&#xff0c;因为它有效地具有多个元素。但是&#xff0c;由于实现tablesorter的方式&#xff0c;这在这种情况下不会有任何帮助。我可以看到在的tablesorter源代码一个未记录的配置选项 - appender - 这…

Idea 创建简单的SpringBoot 父子项目

背景 使用Idea 创建一个模块化的SpringBoot项目&#xff0c;但是发现Idea 创建父子项目的方式较Eclipse 较为不同&#xff0c;且Idea 创建的过程较Eclipse创建父子项目的过程复杂。 Eclipse 创建SpringBoot父子项目传送门 网上虽然有Idea创建SpringBoot父子项目&#xff0c;但…

python曲线拟合预测_用python做曲线拟合

大家好&#xff1f;我是Kepler哎学习&#xff0c;最近在工程中用到曲线拟合。一般我们都是用matlab来做&#xff0c;方便快捷。我们也可以尝试用python编写脚本来拟合数据&#xff0c;方便数据自动化处理。曲线拟合分为一般多项式拟合和指定函数拟合。准备工作&#xff1a;1.建…

微服务架构与SpringCloud

微服务架构简述 通常而言&#xff0c;微服务架构是一种架构模式或者说是一种架构风格&#xff0c;它提倡将单一应用程序划分成一组小的服务&#xff0c;每个服务运行在其独立的自己的进程中&#xff0c;服务之间互相协调、互相配合为用户提供最终价值。服务之间采用轻量级的通…

应用商店应用计算机,基于中国虹计算机的应用商店的设计与实现

摘要&#xff1a;随着互联网的不断发展,第三方应用商店的活跃用户数已达到数亿规模,而且仍然在不断增长,同时各终端厂商也陆续推出了适用于自家终端的应用商店.中国虹计划课题项目经过多年发展,其核心载体中国虹计算机也得到了大量的推广使用,但是中国虹计算机现有的云桌面应用…

python对字符串数组进行排序、依据字符数_Python进行数据的排序以及字符串的操作...

数据异常值的过滤在数据分析中&#xff0c;经常需要检测数据结构中的异常值。我们还是举个例子&#xff0c;先来创建一个包含三列的DataFrame对象&#xff0c;每一列都包含1000个随机数。可以用describe( )函数查看每一列的描述性统计量。例如&#xff0c;你可以会将比标准差大…

REST和RPC是什么东东?两者有什么区别

1 REST与RPC概念 什么是REST REST是一种架构风格&#xff0c;指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。REST规范把所有内容都视为资源&#xff0c;网络上一切皆资源。 REST并没有创造新的技术&#xff0c;组件或服务&#xff0c…

简述计算机进行远程桌面管理操作过程,服务器应用之“远程桌面连接”(操作篇)...

在上篇《服务器应用之“远程桌面连接”(部署篇) 》《服务器应用之“远程桌面连接”(建立篇) 》&#xff0c;我们介绍了远程桌面的部署和建立方法。在远程桌面连接成功后就可以进行一些远程桌面连接可以进行的计算机管理了。具体可以进行的远程桌面连接操作如下&#xff1a;1. 使…

未为dll加载任何符号_专家发现aspersky 和Trend Micro安全性解决方案中的DLL劫持问题...

SafeBreach的研究人员发现了Kaspersky安全连接、Trend Micro最大安全性和Autodesk桌面应用程序中的几个DLL劫持漏洞&#xff0c;黑客可以利用这些漏洞进行DLL预加载、代码执行和权限升级。第一个问题在卡巴斯基安全连接(KSDE) VPN客户端&#xff0c;跟踪为CVE-2019-15689&#…

什么是Eureka? 单机版Eureka如何使用?

Eureka 是什么&#xff1f; Eureka 是Spring Cloud的服务治理组件&#xff0c;有三个核心角色&#xff1a; 服务注册中心、服务提供者、服务消费者。Eureka 主管服务注册中心。 是Netflix的一个子模块&#xff0c;也是核心模块之一。Eureka是一个基于REST的服务&#xff0c;用…

ntko跨浏览器插件_继泄露版后,微软全新 Chrome 内核 Edge 浏览器你都测试过了吗?...

尽管微软的 Office 和 Windows 10 势头不错&#xff0c;但像 WP 手机、XBox 等方面似乎并未获得预期的成功。而先前为了替代老旧的 IE 浏览器&#xff0c;微软倾力开发的 Edge 也未见起色。不过微软也一直在努力变得更加开放&#xff0c;比如发布 VS for Mac、Office for Mac、…

软考计算机英语考题,《全国计算机软考网管英语试题及答案.doc

《全国计算机软考网管英语试题及答案全国计算机软考网管英语试题及答案(1)Heterogeneous network environments consist of computer systems from(1)vendors that run(2)operating systems and communication protocols&#xff0e;An organization that(3)its computer resou…

如何将单机版的Eureka服务改为集群版Eureka服务

Eureka 集群原理 基本原理 上图是来自eureka的官方架构图&#xff0c;这是基于集群配置的eureka&#xff1b; 处于不同节点的eureka通过Replicate进行数据同步Application Service为服务提供者Application Client为服务消费者Make Remote Call完成一次服务调用 服务启动后向…