当前位置: 首页 > news >正文

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实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

相关文章:

  • UE5 右键菜单缺少Generate Visual Studio project files
  • serial靶机教程
  • python-报数(赛氪OJ)
  • Nginx中proxy_pass的斜杠问题(最详细讲解)
  • C++:auto关键字、内联函数、引用、带默认形参值的函数、函数重载
  • Motionface ai工具有哪些?
  • 三相整流电路交流侧谐波仿真分析及计算
  • C语言入门基础题:最大公约数(三个数间取最大公约数)
  • C语言学习
  • vite.config.ts中proxy的rewrite理解
  • 【网络】网络的发展历程及其相关概念
  • 使用Response.Write实现在页面的生命周期中前后台的交互
  • Ubuntu小键盘消失,并且安装好搜狗输入法后无法打出中文的问题
  • 带头双向循环链表(一)
  • 前端(四):前后端分离开发(YAPI的使用)
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • Apache Pulsar 2.1 重磅发布
  • css的样式优先级
  • ES6核心特性
  • Facebook AccountKit 接入的坑点
  • Fundebug计费标准解释:事件数是如何定义的?
  • JAVA之继承和多态
  • node学习系列之简单文件上传
  • PaddlePaddle-GitHub的正确打开姿势
  • PV统计优化设计
  • react-native 安卓真机环境搭建
  • ReactNative开发常用的三方模块
  • Sass 快速入门教程
  • Service Worker
  • Spark学习笔记之相关记录
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 利用DataURL技术在网页上显示图片
  • 我与Jetbrains的这些年
  • 小李飞刀:SQL题目刷起来!
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 在Mac OS X上安装 Ruby运行环境
  • 【云吞铺子】性能抖动剖析(二)
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • #pragma once与条件编译
  • $$$$GB2312-80区位编码表$$$$
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (MATLAB)第五章-矩阵运算
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (十) 初识 Docker file
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .netcore如何运行环境安装到Linux服务器
  • .NET关于 跳过SSL中遇到的问题
  • .NET开发不可不知、不可不用的辅助类(一)
  • .NET中使用Redis (二)
  • .Net转前端开发-启航篇,如何定制博客园主题
  • /var/log/cvslog 太大
  • [ CTF ] WriteUp-2022年春秋杯网络安全联赛-冬季赛
  • [100天算法】-目标和(day 79)