JavaEE之线程池

前面我们了解了多个任务可以通过创建多个线程去处理,达到节约时间的效果,但是每一次的线程创建和销毁也是会消耗计算机资源的,那么我们是否可以将线程进阶一下,让消耗计算机的资源尽可能缩小呢?线程池可以达到此效果,今天我们对 《线程池》 一探究竟!

1. 何为线程池?

线程池(Thread Pool)是一种并发编程的技术,用于在应用程序启动时创建一定数量的线程,并将它们保存在线程池中,以便于任务的执行和线程的重用。当有新任务到来时,线程池会分配一个空闲线程来执行该任务,任务完成后,线程不会被销毁,而是返回线程池中,等待执行下一个任务。

2. 为什么要用线程池?

为什么要用线程池,那就得从线程池的优点来说了
优点:

  1. 重用线程:避免了频繁创建和销毁线程的开销,提高了线程的利用率。
  2. 控制并发度:可以限制并发执行的线程数量,防止系统过载。
  3. 提供线程管理和监控:方便开发人员进行线程的管理和调试。
  4. 提供任务队列:实现任务的缓冲和调度,提升系统响应速度。

3. 为什么使用线程池可以提升效率?

由于线程的频繁创建和销毁会影响计算机效率,所以我们可以减少线程的频繁创建和销毁来提高计算机效率,因为线程池可以实现这种效果,所以可以提升效率,因为线程池做的是少量创建,少量销毁,进而做到了提升效率的功能
线程池最⼤的好处就是减少每次启动、销毁线程的损耗。
从另一种方面来说,计算机分为 内核态(操作系统层面)用户态JVM层面(应用程序层),创建和销毁线程是内核态的操作

4. 线程池怎么用?

Java当中JDK给我们提供了一组针对不同使用场景的线程池实例,如下:

//1.用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将回收并移除缓存
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//2.创建一个操作无界队列且固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//3.创建一个操作无界队列且只有一个工作线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//4.创建一个单线程执行器,可以在给定时间后执行或定期执行
ScheduledExecutorService singleThreadScheduledExecutor=Executors.newSingleThreadScheduledExecutor();
//5.创建一个指定大小的线程池,可以在给定时间后执行或定期执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//6.创建一个指定大少(不传入参数,为当煎机器CPU核心数)的线程池,并行地处理任务
Executors.newWorkStealingPool();

说明:

  • 使用Executors.newFixedThreadPool(3)能创建出固定包含3个线程的线程池.
  • newCachedThreadPool:创建线程数目动态增长的线程池.
  • newFixedThreadPool:创建固定线程数的线程池 ,返回值类型为ExecutorService.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newSingleScheduledThreadPool:创建一个单线程执行器,设定延迟时间后执行命令,或者定期执行命令,是进阶版的Timer.
  • newScheduledThreadPool:创建一个指定大小的线程池,设定延迟时间后执行命令,或者定期执行命令.是进阶版的Timer.
  • 无界:任务队列的容量 没有限制称为无界队列
  • Executors本质上是ThreadPoolExecutor类的封装.
    在这里插入图片描述在这里插入图片描述

5. 自我实现一个线程池

思路:

  1. 首先要描述任务

通过Runnable描述任务

  1. 通过一个阻塞队列管理任务
//阻塞队列组织任务,最多处理100个任务
public static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);
  1. 提供一个方法向线程池提交任务
//添加任务到阻塞队列当中public void submit(Runnable runnable) throws InterruptedException {if(runnable==null){throw new IllegalAccessError("任务为空");}queue.put(runnable);}
  1. 创建多个线程,不停的扫描队列中的任务
//threadNum为初始化时,给定的线程池中含有线程的个数
public MyThreadPool(int threadNum) {if(threadNum<=0){throw new RuntimeException("线程数量必须大于0");}for (int i = 0; i < threadNum; i++) {Thread thread=new Thread(()->{//不停的扫描线程while (true) {try {//从线程中取出任务Runnable runnable = queue.take();//执行任务runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});//启动线程thread.start();}}
  1. 测试我们的线程池是否成功
public class Test {public static void main(String[] args) throws InterruptedException {//创建线程数量为3的线程池MyThreadPool myThreadPool=new MyThreadPool(3);//向线程池提交10个任务for (int i = 0; i < 10; i++) {int taskId=i+1;myThreadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行任务"+taskId+", "+Thread.currentThread().getName());}});}}
}
  1. 完整的自我线程池如下:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class MyThreadPool {//阻塞队列组织任务,最多处理100个任务public static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);//添加任务到阻塞队列当中public void submit(Runnable runnable) throws InterruptedException {if(runnable==null){throw new IllegalAccessError("任务为空");}queue.put(runnable);}public MyThreadPool(int threadNum) {if(threadNum<=0){throw new RuntimeException("线程数量必须大于0");}for (int i = 0; i < threadNum; i++) {Thread thread=new Thread(()->{//不停的扫描线程while (true) {try {//从线程中取出任务Runnable runnable = queue.take();//执行任务runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});//启动线程thread.start();}}
}

6. 创建系统自带的线程池

在这里插入图片描述

执行流程:

  1. 添加任务,核心线程从队列中取任务去执行
  2. 核心线程都在工作时,再添加的任务会进入到阻塞队列
  3. 阻塞队列满了后,会创建临时线程
  4. 阻塞队列满了并且临时线程达到最大,执行拒绝策略
    一次性创建最大的线程数(根据机器的配置,比如CPU核数)

图解:

在这里插入图片描述

说明:

  • corePoolSize: 核心线程的数量.(类似于正式员⼯,⼀旦录⽤,永不辞退)
  • maximumPoolSize:线程池中最大的线程数=核心线程+临时线程的数目.(类似于临时⼯:⼀段时间不⼲活,就被辞退)
  • keepAliveTime:临时线程存活的时间
  • unit:临时线程存活的时间单位,是秒,分钟,还是其他值
  • workQueue:组织(保存)任务的队列
  • threadFactory: 创建线程的工厂,参与具体的创建线程⼯作.通过不同线程工厂创建出的线程相当于对一些属性进行了不同的初始化设置
  • RejectedExecutionHandler: 拒绝策略,如果任务量超出线程池的负荷了接下来怎么处理
    在这里插入图片描述

接下来我们探讨一下这四种拒绝策略:

1. AbortPolicy(): 超过负荷,直接抛出异常.

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestAbortPolicy {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor reject1 =new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject1.submit(() -> {System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

在这里插入图片描述

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接拒绝了,并抛出异常,我不做!!

2. CallerRunsPolicy():返回给调⽤者负责处理多出来的任务.

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestCallerRunsPolicy {public static void main(String[] args) {ThreadPoolExecutor reject2=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject2.submit(()->{System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

在这里插入图片描述

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接拒绝了,老师就自己去做了,保证了任务至少完成了。

3. DiscardOldestPolicy():丢弃队列中最⽼的任务.

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestDiscardOldestPolicy {public static void main(String[] args) {ThreadPoolExecutor reject2=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject2.submit(()->{System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

在这里插入图片描述

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接退出游戏(不处理最老的任务),去完成老师交给我的任务。

4. DiscardPolicy():丢弃新来的任务.

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestDiscardPolicy {public static void main(String[] args) {ThreadPoolExecutor reject2=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.DiscardPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject2.submit(()->{System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

在这里插入图片描述

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接拒绝了,老师也不做,日后可能忘记了该任务,再也找不回来。

总结: 在实际的开发过程中,我们要根据实际的业务场景选择不同的拒绝策略!!!

以上就是线程池的基本知识了,希望对你有帮助,谢谢!!

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

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

相关文章

YOLOv11改进,YOLOv11添加HAttention注意机制用于图像修复的混合注意力转换器,CVPR2023,超分辨率重建

摘要 基于Transformer的方法在低层视觉任务中表现出色,例如图像超分辨率。然而,作者通过归因分析发现,这些网络只能利用有限的空间范围的输入信息。这意味着现有网络尚未充分发挥Transformer的潜力。为了激活更多的输入像素以获得更好的重建效果,作者提出了一种新型的混合…

Shader -> SweepGradient扫描渐变着色器详解

XML文件 <com.example.myapplication.MyViewxmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_gravity"center"android:layout_height"400dp"/>自定义View代码 c…

LabVIEW调用不定长数组 DLL数组

在使用 LabVIEW 调用 DLL 库函数时&#xff0c;如果函数中的结构体包含不定长数组&#xff0c;直接通过 调用库函数节点&#xff08;Call Library Function Node&#xff09; 调用通常会遇到问题。这是因为 LabVIEW 需要与 DLL 中的数据结构完全匹配&#xff0c;而包含不定长数…

IOS开发如何从入门进阶到高级

针对iOS开发的学习&#xff0c;不同阶段应采取不同的学习方式&#xff0c;以实现高效提升.本文将iOS开发的学习分为入门、实战、进阶三个阶段&#xff0c;下面分别详细介绍. 一、学习社区 iOS开源中国社区 这个社区专注于iOS开发的开源项目分享与协作&#xff0c;汇集了大量开…

Next.js 实战 (七):浅谈 Layout 布局的嵌套设计模式

业务场景 在目前常见的中后台管理系统中&#xff0c;比较常见的是固定的布局方式包裹页面&#xff0c;但一些特殊页面&#xff0c;比如&#xff1a;登录页面、注册页面、忘记密码页面这些页面是不需要布局包裹的。 但在 Next.js AppRouter 中&#xff0c;必须包含一个根布局文…

基于 Python 和 OpenCV 的人脸识别上课考勤管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

PHP语言的函数实现

PHP语言的函数实现 在现代Web开发中&#xff0c;PHP是一种流行的后端脚本语言。它以简单易学和强大的功能著称&#xff0c;广泛应用于构建动态网站和Web应用程序。在PHP中&#xff0c;函数是组织代码、提高代码重用性和可读性的关键元素。本文将深入探讨PHP的函数实现&#xf…

人工智能与物联网:智慧城市的未来

引言 清晨6点&#xff0c;智能闹钟根据你的睡眠状态和天气情况&#xff0c;自动调整叫醒时间&#xff1b;窗帘缓缓打开&#xff0c;阳光洒满房间&#xff1b;厨房里的咖啡机已经为你准备好热饮&#xff0c;而无人驾驶公交车正按时抵达楼下站点。这不是科幻电影的场景&#xff…

小R的蛋糕分享

小R的蛋糕分享 问题描述 小R手里有一个大小为 n 行 m 列的矩形蛋糕&#xff0c;每个小正方形区域都有一个代表美味度的整数。小R打算切割出一个正方形的小蛋糕给自己&#xff0c;而剩下的部分将给小S。她希望两人吃的部分的美味度之和尽量接近。 我们定义小R吃到的部分的美味度…

使用postMessage解决iframe与父页面传参

接收传递的消息时&#xff0c;可以将 window.addEventListener(message, function(e) { console.log(e.data) }) 写法&#xff0c;更换为 window.onmessage async function(e) {} 可以避免消息发送后&#xff0c;多次接收该参数 父页面js IframeEvent(){const send …

python-leetcode-无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; class Solution:def lengthOfLongestSubstring(self, s: str) -> int:char_set set()left 0max_length 0for right in range(len(s)):while s[right] in char_set:char_set.remove(s[left])left 1char_…

新版本的IDEA如何解决Git分支显示为警告⚠<unknown>的问题

目录 问题再现 解决思路 首先我们要想到 这个分支有没有从远程代码仓库拉去下来 复习一下 git 命令 其次思考 最后思考 问题再现 这边我使用的是 IDEA 2024.3.3.1 Jetbrains 官网的最新版 同时也是官方账号登录 的 今天上 github 去拉项目到 本地 出现了分支不显示的问…

libusb学习——简单介绍

文章目录 libusb 简介libusb 编译libusb 源码目录介绍核心代码文件平台支持例子 API使用libusb初始化和去初始化libusb设备处理和枚举libusb 杂项libusb USB描述符libusb 设备热插拔事件通知libusb 异步设备I/Olibusb 同步设备I/Olibusb 轮询与定时 libusb 涉及技术参考 libusb…

GRE技术的详细解释

GRE&#xff08;Generic Routing Encapsulation&#xff0c;通用路由封装&#xff09;是一种隧道协议&#xff0c;主要用于在不同网络之间封装和传输其他网络层协议的数据包。它最常用于在IP网络上建立虚拟点到点的隧道连接&#xff0c;是实现VPN的一项关键技术。 下面从原理、…

HarmonyOS 鸿蒙Next 预览pdf文件

HarmonyOS 鸿蒙Next 预览pdf文件 1、使用filePreview 2、使用web组件 在线pdf&#xff08;网址是直接下载的&#xff0c;不是直接可以预览的&#xff09;&#xff0c;先下载再预览 import media from ohos.multimedia.media;import web_webview from ohos.web.webview;import …

案例解读 | 香港某多元化综合金融企业基础监控+网管平台建设实践

PART01 项目背景 01客户简介案例客户是一家创立20多年的香港某多元化综合金融企业&#xff0c;其业务范围涵盖证券、期货、资产管理、财富管理等&#xff0c;凭借广泛的业务网络和多元化的金融服务产品&#xff0c;在市场中拥有显著的影响力。02痛点分析随着业务版图的持续拓展…

551 灌溉

常规解法&#xff1a; #include<bits/stdc.h> using namespace std; int n,m,k,t; const int N105; bool a[N][N],b[N][N]; int cnt; //设置滚动数组来存贮当前和下一状态的条件 //处理传播扩散问题非常有效int main() {cin>>n>>m>>t;for(int i1;i&l…

vue的KeepAlive应用(针对全部页面及单一页面进行缓存)

KeepAlive的作用是缓存包裹在其中的动态切换组件 当一个组件在 中被切换时&#xff0c;它的 activated 和 deactivated 生命周期钩子将被调用&#xff0c;用来替代 mounted 和 unmounted。这适用于 的直接子节点及其所有子孙节点。 缓存全部页面 将app.vue中的路由出口改为&am…

【简博士统计学习方法】第1章:4. 模型的评估与选择

4. 模型的评估与选择 4.1 训练误差与测试误差 假如存在样本容量为 N N N的训练集&#xff0c;将训练集送入学习系统可以训练学习得到一个模型&#xff0c;我们将这么模型用决策函数的形式表达&#xff0c;也就是 y f ^ ( x ) y\hat{f}(x) yf^​(x)&#xff0c;关于模型的拟合…

Lua语言的文件IO

1、我们都知道&#xff0c;在任何语言当中都有输入输出&#xff0c;比如c语言当中就有很多printf,scanf,get ,put,gets,puts,文件io:open,read,write,close,标准io:fopen,fread,fwrite,fclose.在lua语言当中&#xff0c;也有相同的一些输入输出特性&#xff0c;叫io.open,io.re…