[Java线程池]ExecutorService|CompletionService的区别与选择
这段时间对业务系统做了个性能测试,其中使用了较多线程池的技术,故此做一个技术总结。
这次总结的内容比较多,主要是四个:
- ExecutorService
- CompletionService
- Runnable
- Callable
前两个是线程池相关接口,后两个是多线程相关接口。在最后,我会说明什么情况下使用哪个接口,这两类接口如何搭配使用。
Tips:个人拙见,如有不对,请多多指正。
一、ExecutorService
ExecutorService是一个接口,继承自Executor。ExecutorService提供了一些常用操作和方法,但是ExecutorService是一个接口,无法实例化。
不过,Java提供了一个帮助类Executors,可以快速获取一个ExecutorService对象,并使用ExecutorService接口的一些方法。
Executors帮助类提供了多个构造线程池的方法,常用的分为两类:
- 直接执行的
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- 延迟或定时执行的
- newScheduledThreadPool
- newSingleThreadScheduledExecutor
Executors为每种方法提供了一个线程工厂重载。
(一)newCachedThreadPool
创建一个默认的线程池对象,里面的线程和重用,且在第一次使用的时候才创建。可以理解为线程优先模式,来一个创一个线程,直到线程处理完成后,再处理其他的任务。
Code:
package com.macro.boot.javaBuiltThreadPool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class MyExecutorService {public static void main(String[] args) {// 1. 使用帮助类
// ExecutorService executorService = Executors.newCachedThreadPool();// 2. 提交任务
/* for (int i = 0; i < 20; i++) {executorService.submit(new MyRunnable(i));}*/// 3. 重载方法测试test();}private static void test() {// 1. 使用帮助类ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "线程正在执行 --->" + n++);}});// 2. 提交任务for (int i = 0; i < 20; i++) {executorService.submit(new MyRunnable(i));}}
}/*** 1. 线程类*/
class MyRunnable implements Runnable {private int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "正在执行..." + "--->" + id);}
}
输出:几乎是一下子就执行了,newCachedThreadPool会创建和任务数同等匹配的线程,直到处理完成任务的线程可以处理新增的任务。
(二)newFixedThreadPool
Code:创建一个可重用固定线程数量的线程池
package com.macro.boot.javaBuiltThreadPool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 创建一个可固定重用次数的线程池*/
public class MyNewFixedThreadPool {public static void main(String[] args) {
/* // nThreads:线程数量ExecutorService es = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {es.submit(new MyRunnable(i));}*/test();}private static void test() {ExecutorService es = Executors.newFixedThreadPool(5, new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "线程" + n++);}});// 提交任务for (int i = 0; i < 10; i++) {es.submit(new MyRunnable(i));}}
}
(三)newSingleThreadExecutor
只有一个线程(线程安全)
package com.macro.boot.javaBuiltThreadPool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class MyNewSingleThreadExecutor {public static void main(String[] args) throws InterruptedException {
/* ExecutorService es = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {es.submit(new MyRunnable(i));}*/test();}private static void test() throws InterruptedException {ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "线程" + n++);}});for (int i = 0; i < 10; i++) {Thread.sleep(100);es.submit(new MyRunnable(i));}}
}
(四)newScheduledThreadPool
怎么理解这个线程池的延迟时间?很简单,第一次执行的开始时间,加上延迟的时间,就是第二次执行的时间。
package com.macro.boot.ScheduledExecutorService;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class MyScheduledExecutor {public static void main(String[] args) {ScheduledExecutorService sec = Executors.newScheduledThreadPool(4);for (int i = 0; i < 10; i++) {sec.schedule(new MyRunnable(i), 1, TimeUnit.SECONDS);}System.out.println("开始执行。。。");sec.shutdown();}
}class MyRunnable implements Runnable {private int id;@Overridepublic String toString() {return "MyRunnable{" +"id=" + id +'}';}public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "执行了任务" + id);}
}
(五)newSingleThreadScheduledExecutor
newSingleThreadScheduledExecutor和newScheduledThreadPool的区别是,newSingleThreadScheduledExecutor的第二次执行时间,等于第一次开始执行的时间,加上执行线程所耗费的时间,再加上延迟时间,即等于第二次执行的时间。
二、CompletionService
CompletionService是一个接口。
当我们使用ExecutorService启动多个Callable时,每个Callable返回一个Future,而当我们执行Future的get方法获取结果时,会阻塞线程直到获取结果。
而CompletionService正是为了解决这个问题,它是Java8的新增接口,它的实现类是ExecutorCompletionService。CompletionService会根据线程池中Task的执行结果按执行完成的先后顺序排序,任务先完成的可优先获取到。
Code:
package com.macro.boot.completions;import java.util.concurrent.*;public class CompletionBoot {public static void main(String[] args) throws InterruptedException, ExecutionException {// 实例化线程池ExecutorService es = Executors.newCachedThreadPool();ExecutorCompletionService<Integer> ecs = new ExecutorCompletionService<>(es);for (int i = 0, j = 3; i < 20; i++) {ecs.submit(new CallableExample(i, j));}for (int i = 0; i < 20; i++) {// take:阻塞方法,从结果队列中获取并移除一个已经执行完成的任务的结果,如果没有就会阻塞,直到有任务完成返回结果。Integer integer = ecs.take().get();// 从结果队列中获取并移除一个已经执行完成的任务的结果,如果没有就会返回null,该方法不会阻塞。// Integer integer = ecs.poll().get();System.out.println(integer);}// 不要忘记关闭线程池es.shutdown();}
}
class CallableExample implements Callable<Integer> {/*** 使用构造方法获取变量* */private int a;private int b;public CallableExample(int a, int b) {this.a = a;this.b = b;}@Overridepublic Integer call() throws Exception {return a + b;}@Overridepublic String toString() {return "CallableExample{" +"a=" + a +", b=" + b +'}';}
}
三、Runnable
Runnable和Callable两者都是接口,但是也有区别:
- 实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;(重点)
- Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
Code:
class MyRunnable02 implements Runnable {private int i;public MyRunnable02(int i) {this.i = i;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "执行了... ---> " + i);}@Overridepublic String toString() {return "MyRunnable{" +"i=" + i +'}';}
}
四、Callable
Code:
class CallableExample implements Callable<Integer> {/*** 使用构造方法获取变量* */private int a;private int b;public CallableExample(int a, int b) {this.a = a;this.b = b;}@Overridepublic Integer call() throws Exception {return a + b;}@Overridepublic String toString() {return "CallableExample{" +"a=" + a +", b=" + b +'}';}
}
五、Example
本次Demo:使用线程池,循环查询数据库500次。
在最开始的时候,是使用ExecutorServer + Future.get(因为查询数据库肯定需要获取结果,所以必须要用Callable,并且get到结果集)。但是get的阻塞操作,实在是太影响速度了,虽然考虑了两种手段去解决,但是都不了了之。
Code:(只贴线程池的代码,线程类和获取连接的类就不放了)
private void executorServerStart() throws SQLException, ClassNotFoundException, ExecutionException, InterruptedException {// get conTDConUtils tdConUtils = new TDConUtils();Connection con = tdConUtils.getCon();Statement statement = con.createStatement();// SQLString sql = "select last_row(value_double) from db1.tb1;";// ThreadPoolExecutorService es = Executors.newCachedThreadPool();// for eachint count = 500;for (int i = 0; i < count; i++) {Future<ResultSet> submit = es.submit(new MyThread(i, con, sql));ResultSet resultSet = submit.get();// printwhile (resultSet.next()) {System.out.printf("输出:时间:%s,值:%f \n", resultSet.getTimestamp(1), resultSet.getDouble(2));}}es.shutdown();// close resourcestdConUtils.close(con, statement);}
运行时间:8000ms +
改CompletionService:
Code:
private void completionServerStart() throws SQLException, ClassNotFoundException, InterruptedException, ExecutionException {// get conTDConUtils tdConUtils = new TDConUtils();Connection con = tdConUtils.getCon();Statement statement = con.createStatement();// SQLString sql = "select last_row(value_double) from db1.tb1;";// ThreadPoolExecutorService es = Executors.newCachedThreadPool();//构建ExecutorCompletionService,与线程池关联ExecutorCompletionService<ResultSet> ecs = new ExecutorCompletionService<ResultSet>(es);// for eachint count = 500;for (int i = 0; i < count; i++) {ecs.submit(new MyThread(i, con, sql));}for (int i = 0; i < count; i++) {// 通过take获取Future结果,此方法会阻塞ResultSet resultSet = ecs.take().get();while (resultSet.next()) {System.out.printf("输出:时间:%s,值:%f \n", resultSet.getTimestamp(1), resultSet.getDouble(2));}}es.shutdown();tdConUtils.close(con, statement);}
运行时间:300+ms
六、使用小结
分情况。
如果需要获取结果:线程使用Callable;
如果需要异步获取结果:线程池使用CompletionService。
如果不需要获取结果:线程使用Runnable;
如果需要阻塞获取结果:线程池使用ExecutorService。
相关文章:

[Java线程池]ExecutorService|CompletionService的区别与选择
这段时间对业务系统做了个性能测试,其中使用了较多线程池的技术,故此做一个技术总结。 这次总结的内容比较多,主要是四个: ExecutorServiceCompletionServiceRunnableCallable 前两个是线程池相关接口,后两个是多线…...

MySQL-SQL编写练习:基本的SELECT语句
基本的SELECT语句 1. SQL的分类 DDL:数据定义语言。CREATE \ ALTER \ DROP \ RENAME \ TRUNCATEDML:数据操作语言。INSERT \ DELETE \ UPDATE \ SELECT (重中之重)DCL:数据控制语言。COMMIT \ ROLLBACK \ SAVEPOINT \ GRANT \ REVOKE 学习技巧…...
C++经典面试题目(十九)
1、什么是析构函数?它有什么作用? 析构函数是类的特殊成员函数,用于在对象被销毁时执行清理工作。它的名称与类名相同,前面加上波浪号(~)。析构函数的作用在于确保在对象被销毁时释放占用的资源࿰…...
acwing算法提高之图论--SPFA找负环
目录 1 介绍2 训练 1 介绍 本专题用来记录使用spfa算法来求负环的题目。 2 训练 题目1:904虫洞 C代码如下, #include <cstring> #include <iostream> #include <algorithm> #include <queue>using namespace std;typedef p…...

I2C驱动实验:测试I2C驱动是否与设备匹配
一. 简介 前面一篇文章在设备树中创建 ap3216c设备节点信息。 第二篇文章编写了简单的 I2C设备驱动框架,包括 构造 i2c_driver结构体,i2c_driver的注册与注销等。文章如下: I2C驱动实验:向设备树添加 I2C设备的设备节点信息-C…...

5560.树的直径
蛮不错的一道题目,你要利用树的性质分析出,你只需要维护上一次的树的直径的两个端点就好了 #include<iostream>using namespace std; using ll long long; using pii pair<int,int>; const int N 6e510; const int inf 0x3f3f3f3f; cons…...

Decoupled Multimodal Distilling for Emotion Recognition 论文阅读
Decoupled Multimodal Distilling for Emotion Recognition 论文阅读 Abstract1. Introduction2. Related Works2.1. Multimodal emotion recognition2.2. Knowledge distillation3. The Proposed Method3.1. Multimodal feature decoupling3.2. GD with Decoupled Multimodal …...

【css】使用display:inline-block后,元素间存在4px的间隔
问题:在本地项目中使用【display: inline-block】,元素间存在4px间隔。打包后发布到外网又不存在这个问题了。 归根结底这是一个西文排版的问题,英文有空格作为词分界,而中文则没有。 此时的元素具有文本属性,只要标签…...
代码执行漏洞
原理:没有对接口输入的内容进行严格的判断 造成攻击者精心构造的代码非法执行 当应用在调用一些能将字符转化为代码的函数(如PHP中的eval)时,没有考虑用户是否能控 制这个字符串,这就会造成代码执行漏洞。 相 关 函 数 : PHP&…...

SQLServer2022安装
首先从官网上下载2022版本SQL Server 下载 | Microsoft 选择此把呢不能运行,适合我们在学习阶段使用。 同时网页往下滑动,下载SSMS 下载后的文件 注意:在运行时最好获取管理员权限运行,第一次在安装时未获取管理员权限最终…...
vue2 配置@指向src
使用的是vue cli创建的项目。 1.安装 path 如果在 Node.js 环境中运行代码,path 模块默认是可用的,则不需要单独安装,否则输入下面命令安装path npm i path -S 2.找到vue.config.js文件: const { defineConfig } require(vue/…...

用友U9 存在PatchFile.asmx接口任意文件上传漏洞
声明: 本文仅用于技术交流,请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 简介 用友U9是由中国用友软件股份有限公司开发的一款企业…...

如何卸载干净 IDEA(图文讲解)
更新时间 2022-12-20 11:一则或许对你有用的小广告 星球 内第一个项目:全栈前后端分离博客项目,演示地址:Weblog 前后端分离博客, 1.0 版本已经更新完毕,正在更新 2.0 版本。采用技术栈 Spring Boot Mybatis Plus Vue 3.x Vit…...
自动化运维(十)Ansible 之进程管理模块
Ansible的进程管理模块提供了一种强大而灵活的方式来管理和操作各种进程管理器和服务。无论你使用的是Supervisor、Systemd、传统的init脚本还是Runit,这些模块都可以帮助你轻松地管理服务的生命周期。通过合理地使用这些模块,你可以实现服务的自动化管理,提高系统的可靠性和稳…...
【leetcode279】完全平方数,动态规划解法
原问题:给定一个非负整数n,如果把它视作一些完全平方数的和,那么最少需要多少个完全平方数? 这次学习到一个热心网友的解法:把问题转化兑换零钱问题,然后使用动态规划求解。 比如,给定 n12, 那…...
Java 元素排序(数组、List 集合)
数组元素排序 升序 int[] array {3, 1, 4, 5}; Arrays.sort(array);// 升序排序 System.out.println(Arrays.toString(array)); //输出:[1, 3, 4, 5]降序 可以先将数组元素存入 List 集合,然后集合元素逆序,最后将集合元素写回原数组。&a…...

使用vite创建一个react18项目
一、vite是什么? vite 是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成: 一个开发服务器,它基于原生 ES 模块提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR)。 …...
子集(迭代)(leetcode 78)
核心逻辑: 根据子数组包含的元素个数迭代: 现有子集的基础上通过添加这个新元素来翻倍子集的数量 f(n)2f(n−1) vector<vector<int>> subsets(vector<int>& nums) {vector<vector<int>> ans;int i,j,k;ans.p…...

汽车疲劳测试试验平台技术要求(北重厂家)
汽车疲劳测试试验平台技术要求通常包括以下几个方面: 车辆加载能力:测试平台需要具备足够的承载能力,能够同时测试多种车型和不同重量的车辆。 动力系统:测试平台需要具备稳定可靠的动力系统,能够提供足够的力和速度来…...

Redis -- 缓存雪崩问题
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。 可能原因 : 同一时间大量的key到期 ; 解决方案: 给不同的Key的TTL添加随机值 利用Redis集群提高服务的可用性 给缓存业务添加降…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...