Java线程池的这几个大坑,你踩过几个?

首先看一个简单的例子:代码可能会抛出空指针异常,但这个异常就会被吞掉。

图片

要优雅解决问题,可以为线程池设置一个全局的异常处理器,使用自定义线程工厂来设置!

 

java

public class CustomThreadFactory implements ThreadFactory {
private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();

@Override
public Thread newThread(Runnable r) {
Thread thread = defaultFactory.newThread(r);

// 设置全局异常处理器
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("Thread " + t.getName() + " threw exception: " + e.getMessage());
});
return thread;
}

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2, new CustomThreadFactory());
// 提交任务到线程池
executorService.execute(() -> {
throw new NullPointerException("Test Exception");
});
// 关闭线程池
executorService.shutdown();
}
}

拒绝策略设置错误导致接口超时!

如果没有正确设置拒绝策略,可能会导致接口超时或服务中断。

 

java

public class RejectionPolicyExample {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
1, // 核心线程数
1, // 最大线程数
0L, // 空闲线程存活时间
TimeUnit.MILLISECONDS, // 存活时间单位
new LinkedBlockingQueue<>(1), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略
);

// 提交三个任务到线程池,第三个任务将被拒绝
for (int i = 0; i < 3; i++) {
executorService.execute(() -> {
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed task");
});
}

// 关闭线程池
executorService.shutdown();
}
}

合适的拒绝策略如CallerRunsPolicy可以使任务在调用者线程中执行,从而避免任务丢失。

图片

 

java

public class CallerRunsPolicyExample {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
1, // 核心线程数
1, // 最大线程数
0L, // 空闲线程存活时间
TimeUnit.MILLISECONDS, // 存活时间单位
new LinkedBlockingQueue<>(1), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者运行策略
);

// 提交三个任务到线程池,第三个任务将在调用者线程中执行
for (int i = 0; i < 3; i++) {
executorService.execute(() -> {
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed task");
});
}

// 关闭线程池
executorService.shutdown();
}
}

重复创建线程池导致内存溢出

重复创建线程池会导致系统资源浪费,甚至引发内存溢出。

 

java

public class DuplicateThreadPoolExample {
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
ExecutorService executorService = Executors.newFixedThreadPool(10);

// 提交任务到线程池
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + " executed task");
});

// 关闭线程池
executorService.shutdown();
}
}
}

那当然我们整个应用中只使用一个线程池实例。

 

java

public class SingletonThreadPoolExample {
// 单例线程池
private static final ExecutorService executorService = Executors.newFixedThreadPool(10);

public static ExecutorService getExecutorService() {
return executorService;
}

public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
// 使用单例线程池提交任务
getExecutorService().execute(() -> {
System.out.println(Thread.currentThread().getName() + " executed task");
});
}

// 关闭线程池
getExecutorService().shutdown();
}
}

共用线程池执行不同任务

在同一个线程池中执行不同性质的任务,可能会导致任务相互影响,进而降低系统效率。例如,CPU密集型IO密集型任务混用同一线程池,效率会大打折扣。

 

java

public class MixedTasksExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 提交CPU密集型任务
executorService.execute(() -> {
for (int i = 0; i < 1000000; i++) {} // 模拟CPU密集型任务
System.out.println(Thread.currentThread().getName() + " executed CPU-intensive task");
});
// 提交IO密集型任务
executorService.execute(() -> {
try {
Thread.sleep(2000); // 模拟IO密集型任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed IO-intensive task");
});
// 关闭线程池
executorService.shutdown();
}
}

解决方案:

为不同任务分配独立的线程池,确保任务执行的高效性。

 

java

public class SeparateTasksExample {
// CPU密集型任务的线程池
private static final ExecutorService cpuIntensivePool = Executors.newFixedThreadPool(5);
// IO密集型任务的线程池
private static final ExecutorService ioIntensivePool = Executors.newFixedThreadPool(5);

public static void main(String[] args) {
// 提交CPU密集型任务
cpuIntensivePool.execute(() -> {
for (int i = 0; i < 1000000; i++) {} // 模拟CPU密集型任务
System.out.println(Thread.currentThread().getName() + " executed CPU-intensive task");
});

// 提交IO密集型任务
ioIntensivePool.execute(() -> {
try {
Thread.sleep(2000); // 模拟IO密集型任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " executed IO-intensive task");
});

// 关闭线程池
cpuIntensivePool.shutdown();
ioIntensivePool.shutdown();
}
}

ThreadLocal与线程池的冲突

因为线程池中的线程是复用的,ThreadLocal中的变量可能会被其他任务不小心修改未及时清理

 

java

public class ThreadLocalIssueExample {
private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);

// 提交任务到线程池
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
int value = threadLocal.get();
System.out.println(Thread.currentThread().getName() + " initial value: " + value);
threadLocal.set(value + 1);
System.out.println(Thread.currentThread().getName() + " updated value: " + threadLocal.get());
});
}

// 关闭线程池
executorService.shutdown();
}
}

那当然在任务执行完毕后,及时清理ThreadLocal变量

public class ThreadLocalSolutionExample {private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);// 提交任务到线程池for (int i = 0; i < 5; i++) {executorService.execute(() -> {try {int value = threadLocal.get();System.out.println(Thread.currentThread().getName() + " initial value: " + value);threadLocal.set(value + 1);System.out.println(Thread.currentThread().getName() + " updated value: " + threadLocal.get());} finally {// 任务完成后清理ThreadLocal变量threadLocal.remove();}});}// 关闭线程池executorService.shutdown();}
}

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

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

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

相关文章

Vue3从零开始——掌握setup、ref和reactive函数的奥秘

文章目录 一、Vue 3 组合式 API 概述二、setup​ 函数的基本使用2.1 setup​ 函数的特点2.2 setup​ 函数的基本结构2.3 实现一个简单的小demo 三、ref​ 函数的功能和应用3.1 ref​函数介绍3.2 基本使用3.2.1 定义ref​数据3.2.2 修改响应式变量 3.3 使用ref​函数实现计数器 …

LabVIEW工件表面瑕疵识别系统

开发了一种利用LabVIEW和IMAQ Vision视觉工具进行工件表面瑕疵识别的系统。该系统通过图像处理技术识别并分类工件表面的裂纹、划痕等缺陷&#xff0c;从而提升生产线的分拣效率和产品质量。 项目背景 工业生产中&#xff0c;工件表面的缺陷直接影响产品质量和生产效率。传统人…

Go语言加Vue3零基础入门全栈班11 Go语言+gorm用户管理系统实战 2024年08月03日 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728Go语言操作MySQL开发用户管理系统API教程_20240729Redis零基础快速入门_20231227GoRedis开发用户管理系统API实战_20240730Mo…

Linux-入门-02

上节我们讲了如何安装虚拟机,本节课讲一些linux的常用命令,首先我们需要做一些配置,我们的centos的镜像是最小版安装,里面什么也没有,所以我们的linux是不能进行联网的,接下来我们就来一步一步联网 1、配置网络 首先我们需要先使用命令查看ip地址,linux中一切皆文件,只能使用命…

数据结构——排序(1):插入排序

目录 一、排序的概念 二、排列的运用 三、常见的排序算法 四、插入排序 1.直接插入排序 &#xff08;1&#xff09;思路 &#xff08;2&#xff09;过程图示 &#xff08;3&#xff09;代码实现 (4)代码解释 &#xff08;5&#xff09;特性 2.希尔排序 &#xff08;1…

CSS技巧专栏:一日一例 20-纯CSS实现点击会凹陷的按钮

本例图片 案例分析 其实这个按钮非常的简单啊,主要就是利用了box-shadow的inset。 布局代码 <button class="base">凹下的按钮</button> 基础样式 :root{--main-bg-color: #dcdcdc; /* 将页面背景色调整为浅灰色 */--color:#000;--hover-color:#99…

LLM - GQA 之 Group Query Attention 论文与源码精读

目录 Abstract 1.Introduction 2.Method 2.1 UpTraining 2.2 Grouped-query attention 3.Experiments 3.1 Experimental setup 3.2 Main Results 3.3 Ablations 4.Related Work 5.Conclustion 6.Code 6.1 repeat_kv 6.2 Attention Arg Init 6.3 Attention Mat I…

IT服务质量管理攻略(至简)

质量管理、风险管理和信息安全管理是IT服务监督管理的重要内容&#xff0c;三者之间相对独立。IT服务质量管理是通过制订质量方针、质量目标和质量计划&#xff0c;实施质量控制、质量保证和质量改进活动&#xff0c;确保IT服务满足服务级别协议的要求&#xff0c;最终获得用户…

openvidu私有化部署

openvidu私有化部署 简介 OpenVidu 是一个允许您实施实时应用程序的平台。您可以从头开始构建全新的 OpenVidu 应用程序&#xff0c;但将 OpenVidu 集成到您现有的应用程序中也非常容易。 OpenVidu 基于 WebRTC 技术&#xff0c;允许开发您可以想象的任何类型的用例&#xf…

[算法]第一集 递归(未完待续)

递归啊递归&#xff0c;说简单简单&#xff0c;说难难。 首先我们要知道 一、什么是递归&#xff1f; 我们再C语言和数据结构里都用了不少递归&#xff0c;这里就不多详细介绍。 递归简单来说就是函数自己调用自己的情况 二、为什么要用递归呢&#xff1f; 本质来说其实就…

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;32选择#2&#xff1a;33选择#3&#xff1a;34 1.5 检查WRF是否…

Linux 中的信号处理

Linux 中的信号处理是操作系统中非常重要的一个概念&#xff0c;通过信号处理&#xff0c;进程之间可以进行通信、协调以及实现一些重要的功能。本文将从信号的概念、类型、生成、传递、处理、以及常见的信号处理函数等方面展开讨论&#xff0c;以帮助读者更深入地了解 Linux 中…

【机器学习】BP神经网络中的链式法则

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 BP神经网络中的链式法则1. 引言2. 链式法则基础2.1 什么是链式法则&#xff1f;…

29.Labview界面设计(下篇) --- 自定义控件库、界面布局与外观设计

摘要&#xff1a; 题主在上一篇文章中向大家讲解了前面板逻辑框架及结构的搭建和控件的类型介绍&#xff0c;那么本章主要围绕前面板的控件布局以及控件的自定义类型和背景等外观优化项中来讲解。 本篇文章讲解界面设计的下篇内容&#xff0c;上篇内容链接大家可以直接点击链接…

国家统计局中国主要城市面板数据(1990-2023年)

数据说明&#xff1a;数据来源于国家统计局&#xff0c;指标包含&#xff1a;城市、年份、第三产业增加值、第一产业增加值 地区生产总值、第二产业增加值、年末户籍人口、城镇非私营单位在岗职工平均工资 房地产开发投资额、房地产开发住宅投资额、房地产开发办公楼投资额、房…

Linux C 程序 【03】线程栈空间

1.开发背景 上一个篇章创建了线程&#xff0c;参考 FreeRTOS&#xff0c;每个线程都是有自己的内存空间&#xff0c;Linux上面也是一样的&#xff0c;这个篇章主要描述线程栈空间的设置。 2.开发需求 设计实验&#xff1a; 1&#xff09;创建线程&#xff0c;并配置线程内存大…

培训第二十二天(mysql数据库主从搭建)

上午 1、为mysql添加开机启动chkconfig [rootmysql1 ~]# chkconfig --list //列出系统服务在不同运行级别下的启动状态注&#xff1a;该输出结果只显示 SysV 服务&#xff0c;并不包含原生 systemd 服务。SysV 配置数据可能被原生 systemd 配置覆盖。 要列出 systemd 服务…

IEEE报告解读:存储技术发展趋势分析

1.引言 随着数据科学、物联网&#xff08;IoT&#xff09;和永久存储需求的快速增长&#xff0c;对大规模数据存储的需求正在迅速增加。存储技术的发展趋势直接关系到数据的可靠性和经济性。本文将根据IEEE最新发布的《2023年国际器件与系统路线图》&#xff0c;深入探讨各种存…

AnyGPT: Unified Multimodal LLM with Discrete Sequence Modeling

发表时间&#xff1a;arXiv 2024年2月26日 论文链接&#xff1a;https://arxiv.org/pdf/2402.12226 作者单位&#xff1a; Fudan University Motivation&#xff1a; LLM 在理解和生成人类语言方面表现出非凡的能力。但是&#xff0c;LLM 的能力仅限于针对文本的处理。而现…

详解Xilinx FPGA高速串行收发器GTX/GTP(2)--什么是GTX?

文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 GTX本质上是基于SerDes技术的高速串行收发器,它是FPGA内部的底层电路,也叫做Gigabit Transceiver(吉比特收发器,简称为GT)。其中A7系列使用的GT叫GTP,K7系列使用的GT叫GTX,V7系列使用的GT叫GTH和GTZ,它们…