线程池:坑中之王 !

360影视 2024-12-23 08:55 4

摘要:这或许是一个对你有用的开源项目,mall项目是一套基于 SpringBoot3 + Vue 的电商系统(Github标星60K),后端支持多模块和 2024最新微服务架构 ,采用Docker和K8S部署。包括前台商城项目和后台管理系统,能支持完整的订单流程!涵

线程池是 Java 中处理多线程的强大工具,但它不仅仅是“直接用就完事”的工具。

很多小伙伴在用线程池时,因为配置不当或忽略细节,踩过许多坑。

今天跟大家一起聊聊线程池中容易踩的 10 个坑,以及如何避免这些坑,希望对你会有所帮助。

许多初学者在创建线程池时,直接使用 Executors 提供的快捷方法:

ExecutorService executor = Executors.newFixedThreadPool(10);ExecutorService executor = Executors.newFixedThreadPool(2);for (int i = 0; i { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace; } });}

任务数远大于线程数,导致任务无限堆积在队列中,最终可能导致 OutOfMemoryError。

这或许是一个对你有用的开源项目,mall项目是一套基于 SpringBoot3 + Vue 的电商系统(Github标星60K),后端支持多模块和 2024最新微服务架构 ,采用Docker和K8S部署。包括前台商城项目和后台管理系统,能支持完整的订单流程!涵盖商品、订单、购物车、权限、优惠券、会员、支付等功能!

Boot项目:

Cloud项目:

视频教程:

项目演示:

使用 ThreadPoolExecutor,并明确指定参数:

ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(100), // 有界队列 new ThreadPoolExecutor.AbortPolicy // 拒绝策略);

很多人随意配置线程池参数,比如核心线程数 10,最大线程数 100,看起来没问题,但这可能导致性能问题或资源浪费。

ThreadPoolExecutor executor = new ThreadPoolExecutor( 10, // 核心线程数 100, // 最大线程数 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(10));for (int i = 0; i { try { Thread.sleep(5000); // 模拟耗时任务 } catch (InterruptedException e) { e.printStackTrace; } });}

这种配置在任务激增时,会创建大量线程,系统资源被耗尽。

根据任务类型选择合理的线程数:

CPU 密集型:线程数建议设置为 CPU 核心数 + 1。IO 密集型:线程数建议设置为 2 * CPU 核心数。

示例:

int cpuCores = Runtime.getRuntime.availableProcessors;ThreadPoolExecutor executor = new ThreadPoolExecutor( cpuCores + 1, cpuCores + 1, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(50));

任务队列直接影响线程池的行为。如果选错队列类型,会带来很多隐患。

无界队列:任务无限堆积。有界队列:队列满了会触发拒绝策略。优先级队列:容易导致高优先级任务频繁抢占低优先级任务。ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 4, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue);for (int i = 0; i System.out.println(Thread.currentThread.getName));}

改进方法:用有界队列,避免任务无限堆积。

new ArrayBlockingQueue(100);

有些小伙伴用完线程池后,忘记调用 shutdown,导致程序无法正常退出。

ExecutorService executor = Executors.newFixedThreadPool(5);executor.submit( -> System.out.println("任务执行中..."));// 线程池未关闭,程序一直运行正确关闭方式executor.shutdown;try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow; }} catch (InterruptedException e) { executor.shutdownNow;}

当任务队列满时,线程池会触发拒绝策略,很多人不知道默认策略(AbortPolicy)会直接抛异常。

示例:任务被拒绝ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(2), new ThreadPoolExecutor.AbortPolicy // 默认策略);for (int i = 0; i System.out.println("任务"));}

执行到第四个任务时会抛出 RejectedExecutionException。

CallerRunsPolicy:提交任务的线程自己执行。DiscardPolicy:直接丢弃新任务。DiscardOldestPolicy:丢弃最老的任务。

线程池中的任务抛出异常时,线程池不会直接抛出,导致很多问题被忽略。

executor.submit( -> { throw new RuntimeException("任务异常");});解决方法捕获任务内部异常:executor.submit( -> { try { throw new RuntimeException("任务异常"); } catch (Exception e) { System.err.println("捕获异常:" + e.getMessage); }});自定义线程工厂:ThreadFactory factory = r -> { Thread t = new Thread(r); t.setUncaughtExceptionHandler((thread, e) -> { System.err.println("线程异常:" + e.getMessage); }); return t;};

如果线程池中的任务是阻塞的(如文件读写、网络请求),核心线程会被占满,影响性能。

executor.submit( -> { Thread.sleep(10000); // 模拟阻塞任务});改进方法

线程池不是万能的,某些场景直接使用 new Thread 更简单。

一个简单的短期任务:

ExecutorService executor = Executors.newSingleThreadExecutor;executor.submit( -> System.out.println("执行任务"));executor.shutdown;

这种情况下,用线程池反而复杂。

改进方式new Thread( -> System.out.println("执行任务")).start;

很多人用线程池后,不监控其状态,导致任务堆积、线程耗尽的问题被忽略。

System.out.println("核心线程数:" + executor.getCorePoolSize);System.out.println("队列大小:" + executor.getQueue.size);System.out.println("已完成任务数:" + executor.getCompletedTaskCount);

结合监控工具(如 JMX、Prometheus),实现实时监控。

executor.setCorePoolSize(20);executor.setMaximumPoolSize(50);

实时调整线程池参数,能适应业务的动态变化。

线程池是强大的工具,但如果我们日常工作中用得不好也非常容易踩坑。

这篇文章通过实际代码示例,我们可以清楚看到线程池的问题所在及改进方法。

希望这些内容能帮你避免踩坑,写出高质量的线程池代码!

线程池用得好,效率杠杠的;用得不好,程序天天崩!

Github上标星11K的微服务实战项目mall-swarm,全套 视频教程(2024最新版) 来了!全套教程约26小时,共59期,如果你想学习目前最新的微服务技术栈,同时提高自己微服务项目的开发能力的话,不妨了解下,下面是项目的整体架构图,感兴趣的小伙伴可以点击链接 mall-swarm视频教程 加入学习。

来源:散文随风想一点号

相关推荐