Java线程池(更新中)

  1.线程池介绍

顾名思义,线程池就是管理一系列线程的资源池,其提供了一种限制和管理线程资源的方式。每个线程池还维护一些基本统计信息,例如已完成任务的数量。

总结一下使用线程池的好处:

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池一般用于执行多个不相关联的耗时任务,没有多线程的情况下,任务顺序执行,使用了线程池的话可让多个不相关联的任务同时执行。

2.Executor框架介绍

Executor 框架是 Java5 之后引进的,在 Java 5 之后,通过 Executor 来启动线程比使用 Threadstart 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。

this逃逸指的是在对象的构造过程中,当对象还没有完全初始化完成时(构造函数执行完毕),就将该对象的引用传递给了外部环境。这种情况可能会导致对该对象的访问出现不一致性或者线程安全问题。

具体来说,当一个线程在调用对象的构造函数创建对象实例时,如果对象引用在构造函数执行完成之前就被传递给了另一个线程,那么另一个线程可能会访问到尚未完成初始化的对象,这就是this逃逸。

this逃逸可能导致的问题包括:对象状态不一致、线程安全问题、空指针异常等。因此,在编写Java代码时,我们应该避免在构造函数中将this引用传递给外部环境,以确保对象在完全初始化后才能被其他线程访问。

Executor 框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等,Executor 框架让并发编程变得更加简单。

Executor 框架结构主要由三大部分组成:

1、任务(Runnable /Callable)

执行任务需要实现的 Runnable 接口Callable接口Runnable 接口Callable 接口 实现类都可以被 ThreadPoolExecutorScheduledThreadPoolExecutor 执行。

2、任务的执行(Executor)

如下图所示,包括任务执行机制的核心接口 Executor ,以及继承自 Executor 接口的 ExecutorService 接口。ThreadPoolExecutorScheduledThreadPoolExecutor 这两个关键类实现了 ExecutorService 接口。

这里提了很多底层的类关系,但是,实际上我们需要更多关注的是 ThreadPoolExecutor 这个类,这个类在我们实际使用线程池的过程中,使用频率还是非常高的。

注意: 通过查看 ScheduledThreadPoolExecutor 源代码我们发现 ScheduledThreadPoolExecutor 实际上是继承了 ThreadPoolExecutor 并实现了 ScheduledExecutorService ,而 ScheduledExecutorService 又实现了 ExecutorService,正如我们上面给出的类关系图显示的一样。

ThreadPoolExecutor 类描述:

//AbstractExecutorService实现了ExecutorService接口
public class ThreadPoolExecutor extends AbstractExecutorService

ScheduledThreadPoolExecutor 类描述:

//ScheduledExecutorService继承ExecutorService接口
public class ScheduledThreadPoolExecutorextends ThreadPoolExecutorimplements ScheduledExecutorService

3、异步计算的结果(Future)

Future 接口以及 Future 接口的实现类 FutureTask 类都可以代表异步计算的结果。

当我们把 Runnable接口Callable 接口 的实现类提交给 ThreadPoolExecutorScheduledThreadPoolExecutor 执行。(调用 submit() 方法时会返回一个 FutureTask 对象)

Executor 框架的使用示意图

Executor 框架的使用示意图

  • 1.主线程首先要创建实现 Runnable 或者 Callable 接口的任务对象。
  • 2.把创建完成的实现 Runnable/Callable接口的对象直接交给 ExecutorService 执行: ExecutorService.execute(Runnable command))或者也可以把 Runnable 对象或Callable 对象提交给 ExecutorService 执行(ExecutorService.submit(Runnable task)ExecutorService.submit(Callable <T> task))。
  • 3.如果执行 ExecutorService.submit(…)ExecutorService 将返回一个实现Future接口的对象(我们刚刚也提到过了执行 execute()方法和 submit()方法的区别,submit()会返回一个 FutureTask 对象)。由于FutureTask 实现了 Runnable,我们也可以创建 FutureTask,然后直接交给 ExecutorService 执行。
  • 4.最后,主线程可以执行 FutureTask.get()方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。

3.ThreadPoolExecutor类介绍(重要)

线程池实现类 ThreadPoolExecutorExecutor 框架最核心的类。

1.线程池参数分析

ThreadPoolExecutor 类中提供的四个构造方法。我们来看最长的那个,其余三个都是在这个构造方法的基础上产生(其他几个构造方法说白点都是给定某些默认参数的构造方法比如默认制定拒绝策略是什么)。

 /*** 用给定的初始参数创建一个新的ThreadPoolExecutor。*/public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量int maximumPoolSize,//线程池的最大线程数long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间TimeUnit unit,//时间单位BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

下面这些参数非常重要,在后面使用线程池的过程中你一定会用到!所以,务必记清楚。

ThreadPoolExecutor 3 个最重要的参数:

  • corePoolSize : 任务队列未达到队列容量时,最大可以同时运行的线程数量。
  • maximumPoolSize : 任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
  • workQueue: 新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

ThreadPoolExecutor其他常见参数 :

  • keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁。
  • unit : keepAliveTime 参数的时间单位。
  • threadFactory :executor 创建新线程的时候会用到。
  • handler :拒绝策略(后面会单独详细介绍一下)。

下面这张图可以加深你对线程池中各个参数的相互关系的理解

线程池各个参数的关系

线程池各个参数的关系

ThreadPoolExecutor 拒绝策略定义:

如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,ThreadPoolExecutor 定义一些策略:

  • ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
  • ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。
  • ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃掉。
  • ThreadPoolExecutor.DiscardOldestPolicy:此策略将丢弃最早的未处理的任务请求。

        举个例子:Spring 通过 ThreadPoolTaskExecutor 或者我们直接通过 ThreadPoolExecutor 的构造函数创建线程池的时候,当我们不指定 RejectedExecutionHandler 拒绝策略来配置线程池的时候,默认使用的是 AbortPolicy。在这种拒绝策略下,如果队列满了,ThreadPoolExecutor 将抛出 RejectedExecutionException 异常来拒绝新来的任务 ,这代表你将丢失对这个任务的处理。如果不想丢弃任务的话,可以使用CallerRunsPolicyCallerRunsPolicy 和其他的几个策略不同,它既不会抛弃任务,也不会抛出异常,而是将任务回退给调用者,使用调用者的线程来执行任务。

public static class CallerRunsPolicy implements RejectedExecutionHandler {public CallerRunsPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {// 直接主线程执行,而不是线程池中的线程执行r.run();}}}

2.创建线程池的两种方式

方式一:通过ThreadPoolExecutor构造函数来创建(推荐)。

方式二:通过 Executor 框架的工具类 Executors 来创建。

Executors工具类提供的创建线程池的方法如下图所示:

可以看出,通过Executors工具类可以创建多种类型的线程池,包括:

  • FixedThreadPool:固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  • SingleThreadExecutor: 只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
  • CachedThreadPool: 可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
  • ScheduledThreadPool:给定的延迟后运行任务或者定期执行任务的线程池。

《阿里巴巴 Java 开发手册》强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

Executors 返回线程池对象的弊端如下(后文会详细介绍到):

  • FixedThreadPoolSingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM。
  • ScheduledThreadPoolSingleThreadScheduledExecutor:使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
// 无界队列 LinkedBlockingQueue
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}// 无界队列 LinkedBlockingQueue
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}// 同步队列 SynchronousQueue,没有容量,最大线程数是 Integer.MAX_VALUE`
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}// DelayedWorkQueue(延迟阻塞队列)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}

3.线程池常用的阻塞队列

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

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

相关文章

Mac 上安装多版本的 JDK 且实现 自由切换

背景 当前电脑上已经安装了 jdk8; 现在再安装 jdk17。 期望 完成 jdk17 的安装&#xff0c;并且完成 环境变量 的配置&#xff0c;实现自由切换。 前置补充知识 jdk 的安装路径 可以通过查看以下目录中的内容&#xff0c;确认当前已经安装的 jdk 版本。 cd /Library/Java/Java…

框架漏洞RCE-1

一、前提 1、命令执行漏洞&#xff1a;直接调用操作系统命令。攻击者构造恶意命令&#xff0c;将命令拼接到正常的输入中&#xff0c;达到恶意攻击的目的。 (1)、常见命令执行函数 PHP&#xff1a;exec、shell_exec、system、passthru、popen、proc_open、反引号等 ASP.N…

Linux(openEuler、CentOS8)企业内网DHCP服务器搭建(固定Mac获取指定IP)

----本实验环境为openEuler系统<以server方式安装>&#xff08;CentOS8基本一致&#xff0c;可参考本文&#xff09;---- 目录 一、知识点二、实验&#xff08;一&#xff09;为服务器配置网卡和IP&#xff08;二&#xff09;为服务器安装DHCP服务软件&#xff08;三&a…

leetcode-岛屿数量-99

题目要求 思路 1.使用广度优先遍历&#xff0c;将数组中所有为1的元素遍历一遍&#xff0c;遍历过程中使用递归&#xff0c;讲该元素的上下左右四个方向的元素值也置为0 2.统计一共执行过多少次&#xff0c;次数就是岛屿数量 代码实现 class Solution { public:int solve(vec…

ROS 2边学边练(45)-- 构建一个能动的机器人模型

前言 在上篇中我们搭建了一个机器人模型(其由各个关节&#xff08;joint&#xff09;和连杆&#xff08;link&#xff09;组成)&#xff0c;此篇我们会通过设置关节类型来实现机器人的活动。 在ROS中&#xff0c;关节一般有无限旋转&#xff08;continuous&#xff09;,有限旋转…

【工具推荐定制开发】一款轻量的批量web请求命令行工具支持全平台:hey,基本安装、配置、使用

背景 在开发 Web 应用的过程中&#xff0c;作为开发人员&#xff0c;为了确认接口的性能能够达到要求&#xff0c;我们往往需要一个接口压测工具&#xff0c;帮助我们快速地对我们所提供的 Web 服务发起批量请求。在接口联调的过程中&#xff0c;我们通常会用 Postman 等图形化…

python 中如何匹配字符串

python 中如何匹配字符串&#xff1f; 1. re.match 尝试从字符串的起始位置匹配一个模式&#xff0c;如果不是起始位置匹配成功的话&#xff0c;match()就返回none。 import re line"this hdr-biz 123 model server 456" patternr"123" matchObj re.matc…

unity制作app(2)--主界面

1.先跳转过来&#xff0c;做一个空壳&#xff01;新增场景main为4号场景&#xff01; 2.登录成功跳转到四号场景&#xff01; 2.在main场景中新建canvas&#xff0c;不同的状态计划用不同的panel来设计&#xff01; 增加canvas和底图image 3.突然输不出来中文了&#xff0c;浪…

新手做抖音小店,卖什么最容易出单?抖音必爆类目来了!

哈喽&#xff01;我是电商月月 新手做抖音小店没有经验&#xff0c;也不了解市场需求&#xff0c;最好奇的就是&#xff1a;卖什么商品最容易出单&#xff0c;还在犹豫的朋友可以看看这五种类目&#xff0c;在2024年下半年必定火爆一次 一&#xff0e;生活电器类 天气炎热&a…

Objective-C的对象复制与拷贝选项

对象复制与拷贝 文章目录 对象复制与拷贝copy与mutablecopycopy与mutablecopy的简介示例&#xff1a;不可变对象的复制可变对象的复制 NSCopying和NSMutableCopying协议深复刻和浅复刻浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a;深拷贝&#xff08;Deep Copy&…

「Java开发指南」如何用MyEclipse搭建GWT 2.1和Spring?(一)

本教程将指导您如何生成一个可运行的Google Web Toolkit (GWT) 2.1和Spring应用程序&#xff0c;该应用程序为域模型实现了CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 安装Google Eclipse插件为GWT配置一个项目搭建从数据库表到一个现有的项目GWT编译…

动态规划——路径问题:931.下降路径最小和

文章目录 题目描述算法原理1.状态表示&#xff08;经验题目&#xff09;2.状态转移方程3.初始化4.填表顺序5.返回值 代码实现CJava 题目描述 题目链接&#xff1a;931.下降路径最小和 关于这⼀类题&#xff0c;看过我之前的博客的朋友对于状态表示以及状态转移是⽐较容易分析…

libcity 笔记:libcity/data/utils.py

1 get_dataset 2 list_dataset.py/ListDataset from torch.utils.data import Datasetclass ListDataset(Dataset):def __init__(self, data):"""data: 必须是一个 list"""self.data datadef __getitem__(self, index):return self.data[index…

照片格式怎么转换jpg?利用在线图片处理工具完成操作

图片有许多不同的格式类型&#xff0c;其中我们最常见的是jpg和png等。通常在平台上上传图片时&#xff0c;大多数要求使用jpg格式较多&#xff0c;但你知道吗&#xff1f;不同的设备和软件可能有不同的默认保存格式。如果你发现你的照片不是jpg格式&#xff0c;该如何转换呢&a…

为何预测预测蛋白质结构这么重要AlphaFold 3;阿里巴巴的开源语音转文字;抱抱脸开源LeRobot

✨ 1: AlphaFold 3 谷歌DeepMind和同构实验室推出AlphaFold 3 AI模型&#xff0c;旨在精确预测生命分子的结构和相互作用。 AlphaFold 3 是由谷歌DeepMind和Isomorphic Labs开发的一款新型AI模型&#xff0c;它可以以前所未有的精确度预测蛋白质、DNA、RNA、配体&#xff08;…

Java12基础(Package包 作用域 String字符串)

目录 一. Package包 import关键字 命名规范 二. 作用域 三. String字符串(进阶) 创建方式: 内存情况: 1. 字符串的搜索 2. trim()方法 3. 替换字符串 4. 分割字符串 5. 拼接字符串 6. 格式化字符串 7. 类型转换 8. 转换为char[ ]字符数组 9. 字符编码 10. Str…

【C++】C/C++中新const用法:const成员

欢迎来到CILMY23的博客 本篇主题为&#xff1a; C/C中新const用法&#xff1a;const成员 个人主页&#xff1a;CILMY23-CSDN博客 系列专栏&#xff1a;Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux 感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞…

医院预约挂号|基于Springboot+vue的医院预约挂号系统小程序的设计与实现(源码+数据库+文档)

医院预约挂号系统小程序 目录 基于Springboot&#xff0b;vue的医院预约挂号系统小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 1小程序端 后台功能模块 4.2.1管理员功能 4.2.2医生功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

linux查看ip和端口

1. ip addr ip addr 或者 ip addr show 输出包含了网络接口的名称、状态、MTU&#xff08;Maximum Transmission Unit&#xff09;、链路层地址&#xff08;如MAC地址&#xff09;、IPv4和IPv6地址等信息。 2. 只需要 ip地址 ipV4 ip addr | grep inet ipV6 3.查看端口 s…

重磅更新:Facefusion换脸软件intel显卡专用版本整合包更新了

重磅更新&#xff01;Facefusion换脸软件intel显卡专用版本&#xff0c;I卡用户也可以体验视频换脸了&#xff0c;独家针对Intel显卡优化&#xff0c;intel显卡视频换脸速度也很快&#xff0c;比cpu运行速度快十倍左右 目前测试Facefusion换脸工具intel显卡专用版本在A310、A3…