Java线程池的使用和最佳实践

第1章:引言

处理并发问题时,如果每次都新建线程,那系统的压力得有多大?这时候,线程池就像一个英雄一样出现了,它帮我们有效地管理线程,提高资源利用率,降低开销。那么,为什么说线程池这么重要呢?首先,线程池能够控制系统中执行线程的数量,这样就减少了线程创建和销毁的开销,提高了系统的响应速度。其次,通过合理的配置,线程池能够提供更好的系统稳定性,避免因为线程数量过多而导致系统崩溃。

小黑今天就带咱们一起深入了解Java中的线程池,看看它是怎么回事,怎么用,以及如何在我们的代码里发挥最大的效力。

第2章:线程池的基本原理

要想彻底搞懂线程池,咱们得先弄明白它的基本原理。线程池,顾名思义,就是存放线程的池子。但它不仅仅是简单的存放,更重要的是它对线程进行了有效的管理。在Java中,线程池通过Executor接口和其实现类ThreadPoolExecutor来提供。

说到底,线程池的核心思想就是复用已有线程。当任务来临时,线程池会尝试使用已存在的线程,而不是每次都新建。如果所有线程都在忙,线程池会根据配置决定是创建新线程,还是放到一个队列中等待。这就大大减少了线程创建和销毁的开销,提高了响应速度。

现在,咱们来看一段示例代码,理解线程池的创建和使用:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {// 创建一个固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {Runnable worker = new WorkerThread("" + i);executor.execute(worker);}executor.shutdown();while (!executor.isTerminated()) {}System.out.println("所有任务已完成");}
}class WorkerThread implements Runnable {private String command;WorkerThread(String s) {this.command = s;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 开始. 命令 = " + command);processCommand();System.out.println(Thread.currentThread().getName() + " 结束.");}private void processCommand() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic String toString() {return this.command;}
}

在这个例子中,咱们创建了一个固定大小的线程池,并提交了10个任务。这就是线程池的魅力所在:管理和复用线程,让咱们的程序更加高效。

PS: 小黑收集整理了一份超级全面的复习面试资料包,在这偷偷分享给你~
链接:https://sourl.cn/gUV3UP 提取码:fjb3

第3章:Java中的线程池类型

Java提供了几种不同类型的线程池,每种都有它的特点和用途。这一章节,小黑要带大家了解这些类型,看看它们各自适合什么场景。

1. 固定数量线程池(FixedThreadPool)

先说说FixedThreadPool。顾名思义,这种线程池的线程数量是固定的。它适合于负载相对平稳的场景,线程数量不变意味着不会频繁地创建和销毁线程,效率比较高。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
// 使用这个线程池来执行任务
2. 可缓存线程池(CachedThreadPool)

然后是CachedThreadPool,这个线程池可以根据需要创建新线程,但如果之前创建的线程可用,就会重用它们。它非常适合于任务数量动态变化的场景。

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 适用于任务数动态变化的情况
3. 单线程化线程池(SingleThreadExecutor)

SingleThreadExecutor,这个线程池里只有一个线程在工作。它保证了所有任务都在同一个线程按顺序执行,这对于需要保证执行顺序的场景非常有用。

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 用于需要顺序执行任务的场景
4. 定时及周期性任务线程池(ScheduledThreadPool)

最后是ScheduledThreadPool,这个线程池特别适合需要执行定时或周期性任务的场景。你可以设定任务在指定的延迟后执行,或者定期执行。

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 使用这个线程池来执行定时或周期性任务

咱们看到,Java给我们提供了不同类型的线程池,每一种都有其独特的使用场景。选择正确的线程池类型,可以大大提高程序的性能和稳定性。不过记住,不同类型的线程池适用于不同的应用场景,咱们在选择时要根据实际情况来定。这样,咱们就能把线程池的潜力发挥到极致!

第4章:创建自定义线程池

接下来小黑要和大家聊聊如何创建自定义线程池。虽然Java提供了几种现成的线程池,但有时候咱们需要根据具体情况来定制自己的线程池。这就需要用到ThreadPoolExecutor类。

ThreadPoolExecutor类提供了丰富的构造器,让我们可以精细地控制线程池的行为,比如线程数量、存活时间、工作队列等。现在,小黑就来给大家展示一下如何使用这个类来创建一个符合自己需求的线程池。

首先,咱们来看看ThreadPoolExecutor构造器的参数:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
  • corePoolSize:核心线程数,即即使线程是空闲的,线程池也会保持存活的线程数。
  • maximumPoolSize:线程池允许的最大线程数。
  • keepAliveTime:当线程数超过核心线程数时,多余的空闲线程的存活时间。
  • unitkeepAliveTime的时间单位。
  • workQueue:工作队列,用于存放待执行的任务。
  • threadFactory:线程工厂,用于创建线程。
  • handler:拒绝策略,当线程池和工作队列都满了,如何处理新加入的任务。

下面是一个创建自定义线程池的示例:

import java.util.concurrent.*;public class CustomThreadPoolExample {public static void main(String[] args) {int corePoolSize = 5;int maxPoolSize = 10;long keepAliveTime = 5000;ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());// 提交任务到线程池for (int i = 0; i < 20; i++) {executor.execute(new Task("" + i));}executor.shutdown();}
}class Task implements Runnable {private String name;public Task(String name) {this.name = name;}public void run() {System.out.println("Executing : " + name);}
}

在这个例子中,小黑创建了一个核心线程数为5,最大线程数为10的线程池。线程空闲时间设置为5000毫秒,使用了LinkedBlockingQueue作为工作队列,当线程池和队列都满了的时候,使用CallerRunsPolicy策略。

通过这种方式,咱们就可以根据实际需求创建一个最适合自己的线程池了。记住,合理配置线程池的参数对于提高程序的性能和稳定性至关重要。

第5章:线程池的关键配置及其最佳实践

走到这一步,咱们已经知道了如何创建线程池,那接下来小黑要和大家聊聊线程池的关键配置和一些最佳实践。这些配置和实践可以帮助咱们更好地使用线程池,提高程序的性能和稳定性。

核心线程数(corePoolSize)

核心线程数是线程池中始终保持活跃的线程数量,即使它们没有任务在执行。设置这个值时,咱们要考虑到系统资源的限制和任务的实际需求。如果设置得太高,可能会浪费系统资源;设置得太低,又可能导致处理能力不足。

最大线程数(maximumPoolSize)

最大线程数定义了线程池可以创建的最大线程数量。当工作队列满了之后,线程池会开始创建新线程,直到达到这个数值。合理的设置这个值对于防止系统过载非常重要。

空闲线程的存活时间(keepAliveTime)

当线程池中线程数量超过核心线程数时,多余的线程会在空闲一定时间后被终止,这个时间就是空闲线程的存活时间。这个配置可以帮助系统在不忙碌的时候释放资源。

工作队列(workQueue)

工作队列用于存放等待执行的任务。队列的类型对于线程池的行为有很大影响。例如,LinkedBlockingQueue通常用于固定大小的线程池,而SynchronousQueue适用于缓存线程池。

拒绝策略(RejectedExecutionHandler)

当线程池和工作队列都满了,我们必须定义一个拒绝策略来处理新加入的任务。Java提供了几种标准的拒绝策略,如AbortPolicy(抛出异常)、CallerRunsPolicy(在调用者的线程中执行任务)等。

最佳实践
  1. 正确估算线程需求:根据任务的性质(CPU密集型、IO密集型)和系统环境来合理设置核心线程数和最大线程数。
  2. 合理选择工作队列:根据任务的数量和类型选择适合的队列类型。
  3. 合理配置拒绝策略:根据业务需求选择合适的拒绝策略。
  4. 监控线程池状态:定期监控线程池的状态,包括线程数量、活跃度、任务数量等,以便及时调整配置。

通过以上这些配置和实践,咱们可以更有效地管理线程池,确保应用程序的高效稳定运行。记住,没有一成不变的规则,关键在于根据实际情况灵活调整。

第6章:线程池的常见问题及解决策略

本章和大家探讨一下线程池可能遇到的一些常见问题以及解决这些问题的策略。理解这些问题及其解决方法对于确保线程池稳定高效地运行至关重要。

线程池的常见问题
  1. 线程饥饿死锁:当线程池中的线程都在等待其他任务完成,而这些任务也需要线程池中的线程来执行时,就会发生线程饥饿死锁。

  2. 资源耗尽:如果线程池的最大线程数设置得过高,可能会耗尽系统资源,导致性能下降,甚至崩溃。

  3. 任务拒绝:当线程池满了且工作队列也满时,新提交的任务会被拒绝。

  4. 线程泄露:在某些情况下,线程可能因为未能正确处理异常而永远卡在某个状态,导致线程泄露。

解决策略
  1. 避免线程饥饿死锁:合理配置核心线程数和最大线程数,确保有足够的线程来处理任务。另一种策略是使用不同的线程池来处理不同类型的任务。

  2. 资源管理:合理设置最大线程数和工作队列的大小,以避免资源耗尽。监控系统的性能指标,如CPU和内存使用率,可以帮助及时调整线程池配置。

  3. 合理的拒绝策略:选择合适的拒绝策略,如CallerRunsPolicy,可以让提交任务的线程自己执行该任务,从而降低对线程池的压力。

  4. 异常处理:确保任务执行过程中的异常被妥善处理,避免线程因异常而无法继续执行其他任务。

让我们来看一个简单的示例,展示如何处理任务执行中的异常:

public class SafeTask implements Runnable {@Overridepublic void run() {try {// 执行任务的逻辑} catch (Exception e) {// 处理异常}}
}// 使用线程池执行任务
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new SafeTask());

在这个例子中,SafeTask类中的run方法内部处理了可能发生的异常,这样即使在执行任务时出现异常,线程也不会因此退出,而是可以继续执行其他任务。

理解并解决这些常见问题,将有助于我们更好地利用线程池,保持应用程序的稳定和高效。

第7章:实际案例分析

接下来小黑要和大家分享一些实际的线程池使用案例。通过这些案例,咱们可以更好地理解线程池在实际项目中是如何发挥作用的。

案例一:Web服务器处理请求

想象一下,咱们有一个Web服务器,它需要处理成百上千的并发请求。如果为每个请求创建一个新线程,系统很快就会因为线程过多而崩溃。这时,线程池就派上用场了。

// 创建一个固定大小的线程池
ExecutorService pool = Executors.newFixedThreadPool(100);// 模拟处理请求
for (int i = 0; i < 1000; i++) {pool.execute(new HttpHandler());
}// HttpHandler类处理实际的请求
class HttpHandler implements Runnable {public void run() {// 处理HTTP请求的逻辑}
}

在这个案例中,线程池限制了同时处理的请求数量,保证了系统的稳定性。

案例二:数据处理和分析

假设小黑现在有一个任务是处理大量数据并进行分析。这些数据处理任务是独立的,可以并行执行以提高效率。

// 创建一个可缓存的线程池
ExecutorService pool = Executors.newCachedThreadPool();// 模拟数据处理任务
for (Data data : dataList) {pool.execute(new DataProcessor(data));
}// DataProcessor类处理数据
class DataProcessor implements Runnable {private Data data;DataProcessor(Data data) {this.data = data;}public void run() {// 数据处理逻辑}
}

在这个案例中,可缓存的线程池可以根据需要创建新线程,从而提高了数据处理的效率。

通过这些案例,咱们可以看到,线程池在不同场景下如何有效地提高系统性能,同时保证稳定性和可靠性。记住,理论知识很重要,但将知识应用到实际问题中才能真正理解和掌握它。

第8章:总结

线程池是Java并发编程中非常强大的工具,它能有效地管理线程,提高资源利用率,增强程序的响应速度。但同时,合理配置和使用线程池也非常关键,这关系到程序的性能和稳定性。咱们在使用线程池时,要考虑到核心线程数、最大线程数、工作队列、线程存活时间以及拒绝策略等多个方面。


面对寒冬,我们更需团结!小黑收集整理了一份超级强大的复习面试资料包,也强烈建议你加入我们的Java后端报团取暖群,一起复习,共享各种学习资源,互助成长。无论是新手还是老手,这里都有你的位置。在这里,我们共同应对职场挑战,分享经验,提升技能,闲聊副业,共同抵御不确定性,携手走向更稳定的职业未来。让我们在Java的路上,不再孤单!进群方式以及资料,点击如下链接即可获取!

链接:https://sourl.cn/gUV3UP 提取码:fjb3

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

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

相关文章

代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理 &#xff08;一&#xff09;定义 1、定义 为其他对象提供一种代理以控制对这个对象的访问&#xff1b; 2、涉及到的角色 &#xff08;1&#xff09;抽象主题角色&#xff1a;真实主题和代理主题的共同接口&#xff0c;便于在使用真实主题的地方都可以使用代理…

C#网络编程System.Net.WebClient 类vs System.Net.Http.HttpClient 类

目录 一、WebClient 类 1.WebClient 将数据上传到资源的方法 2.WebClient 从资源下载数据的方法 3.示例源码 4.生成效果 二、HttpClient 类 1.示例源码 2.生成效果 为什么要把两者拿出来pk呢&#xff1f;那是因为WebClient已经在.NET 6.0以后得版本被弃用了&#xff0c…

python跑ncnn(验证模型是否转换成功)

为了转ncnn模型是否成功&#xff0c;用python验证一下先 pip install ncnn分割模型的验证代码 import ncnn import cv2 import numpy as np# 创建ncnn的网络对象 net ncnn.Net()# 加载ONNX模型 net.load_param(E:\\Android_Projects\\ncnn-android-deeplabv3plus-main\\app\…

Spring学习笔记:Day1

学习策略&#xff1a; 理论与实践相结合&#xff1a;每学完一个知识点&#xff0c;尝试进行小的练习或小的项目实践。 持续反馈&#xff1a;在学习过程中&#xff0c;参与社区、论坛或与同事进行讨论&#xff0c;及时解决遇到的问题。 实际项目驱动&#xff1a;在学习后期&a…

PHP字符串面试题

PHP字符串面试题 1. 创建一个字符串变量&#xff0c;并打印输出字符串的内容。 <?php $string "Hello, World!"; echo $string; ?>2. 如何获取字符串的长度&#xff1f; <?php $string "Hello, World!"; $length strlen($string); echo …

P1530 [USACO2.4] 分数化小数 Fractions to Decimals

题目描述 写一个程序,输入一个形如 DN​ 的分数,输出它的小数形式。如果小数有循环节的话,把循环节放在一对圆括号中。 例如,13=0.33333333…31​=0.33333333… 写成 0.(3)0.(3),41333=0.123123123…33341​=0.123123123… 写成 0.(123)0.(123),整数 x 写成 x.0。 输入…

Vue3父子组件通信

一、父组件给子组件传值 子组件 ShipHomePortDialog 1.定义变量名称 <script lang"ts" setup> const props defineProps([title]) </script> 2.在template中使用变量 <h4>{{ title }}</h4> 3.在Script代码使用 var t props.title…

鸿蒙开发笔记

最近比较火&#xff0c;本身也是做前端的&#xff0c;就抽空学习了下。对前端很友好 原视频地址&#xff1a;黑马b站鸿蒙OS视频 下载安装跟着视频或者文档就可以了。如果你电脑上安装的有node&#xff0c;但是开发工具显示你没安装&#xff0c;不用动咱们的node&#xff0c;直…

15个Pandas代码片段助力数据分析

大家好&#xff0c;Python的Pandas库是数据分析的基本工具&#xff0c;提供了强大的数据操作和分析功能。本文将探讨15个高级Pandas代码片段&#xff0c;这些代码片段将帮助简化数据分析任务&#xff0c;并从数据集中提取有价值的见解。 1. 过滤数据 import pandas as pd# 创…

红队攻防实战之Access注入

若盛世将倾&#xff0c;深渊在侧&#xff0c;我辈当万死以赴 访问漏洞url: 1.Access联合查询 判断是否有注入 and 11正常&#xff0c;and 12出错 判断字段数 order by 7正常 order by 8出错 爆破出表名并判断回显点为2&#xff0c;5 查看字段内容&#xff0c;将字段名填入回…

12月1号作业

实现运算符重载 #include <iostream>using namespace std; class Person{friend const Person operator-(const Person &L,const Person &R);friend bool operator<(const Person &L,const Person &R);friend Person operator-(Person &L,const …

WakaTime一个用于跟踪和分析编程时间的工具

WakaTime是一个用于跟踪和分析编程时间的工具&#xff0c;它可以集成到各种代码编辑器和集成开发环境中&#xff0c;例如Visual Studio Code、Sublime Text、PyCharm等。它可以帮助开发人员了解他们花费在不同项目和编程语言上的时间&#xff0c;以及他们的编码习惯和生产力。 …

C语言实战演练之贪吃蛇游戏

贪吃蛇游戏的C语言实现主要包括以下几个步骤&#xff1a; 1. 初始化游戏界面&#xff1a; 设置窗口大小&#xff0c;背景颜色等。 2. 创建蛇的数据结构&#xff1a; 包括蛇头的位置、蛇身的长度、蛇身的坐标等。 3. 创建食物的数据结构&#xff1a; 包括食物的位置等。 4. 控制…

【面试HOT200】二叉树——广度优先搜索篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于【CodeTopHot200】进行的&#xff0c;每个知识点的修正和深入主要参…

Leetcode 2952. Minimum Number of Coins to be Added

Leetcode 2952. Minimum Number of Coins to be Added 1. 解题思路2. 代码实现 题目链接&#xff1a;2952. Minimum Number of Coins to be Added 1. 解题思路 这一题思路上就是一个贪婪算法的思路&#xff0c;偏数学性多一点。 首先&#xff0c;我们将面值有序排列&#x…

SpringSecurity工作原理

实现功能就是继承这几个对应功能的类。 大概工作流程 Spring Security 的过滤器&#xff08;Filters&#xff09;和拦截器&#xff08;Interceptors&#xff09;是 Spring Security 框架中用于保护 web 应用安全的重要组件。它们在处理 HTTP 请求时扮演不同的角色&#xff0c…

Go 模块系统最小版本选择法 MVS 详解

目录 Golang 模块系统简介 包版本管理 最小版本选择&#xff08;MVS&#xff09;原理 MVS 的优点 MVS的缺点 实际使用MVS 小结 参考资料 Golang 模块系统简介 Golang 模块系统是 Go 1.11 版本引入的一个新特性&#xff0c;主要目的是解决 Go 项目中的依赖管理问题。在模…

Python IDLE: 一个简单易用的Python集成开发环境

简介 Python是一种高级编程语言&#xff0c;被广泛用于科学计算、数据分析、Web开发等领域。要编写和运行Python代码&#xff0c;您需要一个集成开发环境&#xff08;Integrated Development Environment&#xff0c;简称IDE&#xff09;。Python IDLE&#xff08;Integrated …

数学杂谈:残次品的无砝码天平定位问题

数学杂谈&#xff1a;残次品的无砝码天平定位问题 1. 问题描述2. 问题解答3. 问题拓展 1. 引理12. 引理23. 引理34. 推论15. 推论2 1. 问题描述 给出问题如下&#xff1a; 12个乒乓球&#xff0c;有一个次品&#xff0c;不知轻重&#xff0c;用一台无砝码天平称三次&#xf…