线程配置经验
工作时,时常会遇到,线程相关的问题与解法,本人会持续对开发过程中遇到的关于线程相关的问题及解决记录更新记录在此篇博客中。
目录
一、线程基本知识
1. 线程和进程
二、问题与解法
1. 避免乘法级别数量线程并行
1)使用线程池
2)任务队列
3)限制并发任务数量
4)任务分批处理
2. 避免函数被重复调用
1)使用标志位
2)使用同步锁
3)使用AOP和缓存
4)使用消息队列
5)使用限流算法
一、线程基本知识
1. 线程与进程
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。例如,任务管理器显示的就是进程:

线程是比进程更小的执行单位。一个进程在其执行过程中可以产生多个线程。与进程不同的是同类的多个线程共享堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。
JDK 1.2之前,Java线程是基于绿色线程实现的,这是一种用户级线程。即JVM自己模拟了多线程的运行,而不依赖于操作系统。由于绿色线程和原生线程比起来的在使用时有一些限制(比如,绿色线程不能直接使用操作系统提供的功能如异步 I/O、只能在一个内核线程上运行无法利用多核);在JDK 1.2之后,Java线程改为使用原生线程来实现。也就是说 JVM 直接使用操作系统原生的内核级线程(内核线程)来实现 Java 线程,由操作系统内核进行线程的调度和管理。
- 用户线程:由用户空间程序管理和调度的线程,运行在用户空间(专门给应用程序使用)。
- 内核线程:由操作系统内核管理和调度的线程,运行在内核空间(只有内核程序可以访问)。
顺便简单总结一下用户线程和内核线程的区别和特点:用户线程创建和切换成本低,但不可以利用多核。内核态线程,创建和切换成本高,可以利用多核。现在的 Java 线程的本质其实就是操作系统的线程。
线程模型是用户线程和内核线程之间的关联方式,常见的线程模型有这三种:
- 一对一(一个用户线程对应一个内核线程)
- 多对一(多个用户线程映射到一个内核线程)
- 多对多(多个用户线程映射到多个内核线程)

在 Windows 和 Linux 等主流操作系统中,Java 线程采用的是一对一的线程模型,也就是一个 Java 线程对应一个系统内核线程。
一个进程中可以有多个线程,多个线程共享进程的堆和方法区(JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
- 程序计数器为什么私有:程序计数器是为了线程切换后,能够回到正确的执行位置。
- 虚拟机栈为什么私有:每个Java栈帧在执行之前会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至完成的过程,对应着一个栈帧在Java虚拟机栈中入栈和出栈的过程。
- 本地方法栈为什么私有:和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象(几乎所有对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
二、问题与解法
1. 避免乘法级别数量线程并行
当一个多线程函数被重复调用,就造成了并行线程数量成为乘法级别。
这种情况通常不是故意的,而是不小心。
一旦发生这种情况,需要及时排查,不然线程太多、资源混乱,服务器上其他服务请求被搁置,就变成了事故。
例如:一个大批量执行、耗时、数据量大的任务。现在使用三个线程、并行生成结果。这个函数 每三分钟执行一次。可能下一次调用开始时,旧的线程还没执行结束。
count (并行线程数)= m(调用次数) * n(单词调用线程数)
乘法级别的线程在处理这些数据,不仅不能提高处理效率、反而由于线程太多造成了资源混乱、大量资源浪费。导致正常的请求无法及时被相应,影响到服务器上其他服务的正常使用。甚至可能导致服务器宕机。
可以使用线程池、任务队列、限制并发任务数量、任务分批处理来解决。
1)使用线程池
线程池可以有效管理线程的生命周期,避免频繁创建和销毁线程带来的开销,并且可以限制并发线程的数量。Java提供了 ExecutorService 来实现线程池。
注意,线程池多线程调用也会出现乘法级别数量的线程
示例代码:
import java.util.concurrent.*;public class TaskScheduler {private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);private final ExecutorService threadPool = Executors.newFixedThreadPool(3);public void scheduleTask(Runnable task) {scheduler.scheduleAtFixedRate(() -> {try {// 提交任务到线程池for (int i = 0; i < 3; i++) {threadPool.submit(task);}} catch (RejectedExecutionException e) {// 处理线程池拒绝任务的情况System.out.println("Task rejected, possibly due to shutdown or overload.");}}, 0, 3, TimeUnit.MINUTES);}public void shutdown() {scheduler.shutdown();threadPool.shutdown();}public static void main(String[] args) {TaskScheduler scheduler = new TaskScheduler();Runnable task = () -> {// 你的任务逻辑System.out.println("Task executed by " + Thread.currentThread().getName());};scheduler.scheduleTask(task);// 程序结束时关闭线程池Runtime.getRuntime().addShutdownHook(new Thread(scheduler::shutdown));}
}
优点:
-
限制线程数量:通过固定大小的线程池,可以避免线程过多导致的资源竞争。
-
任务调度:
ScheduledExecutorService可以定时执行任务,避免任务重叠。 -
优雅关闭:通过
shutdown()方法可以优雅地关闭线程池。
2)任务队列
如果任务的执行时间不确定,可以使用任务队列来管理任务。线程池会从任务队列中获取任务并执行。
import java.util.concurrent.*;public class TaskQueueExample {private final ExecutorService threadPool = Executors.newFixedThreadPool(3);private final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();public void addTask(Runnable task) {try {taskQueue.put(task);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}public void startProcessing() {for (int i = 0; i < 3; i++) {threadPool.submit(() -> {while (!Thread.currentThread().isInterrupted()) {try {Runnable task = taskQueue.take();task.run();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}});}}public void shutdown() {threadPool.shutdown();}public static void main(String[] args) throws InterruptedException {TaskQueueExample example = new TaskQueueExample();example.startProcessing();// 模拟添加任务for (int i = 0; i < 10; i++) {example.addTask(() -> {System.out.println("Task executed by " + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟耗时任务} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 程序结束时关闭线程池Runtime.getRuntime().addShutdownHook(new Thread(example::shutdown));}
}
优点:
-
任务排队:任务会被放入队列,按顺序执行,避免任务重叠。
-
线程复用:线程池中的线程会复用,减少线程创建和销毁的开销。
3)限制并发任务数量
如果任务的执行时间较长,可以限制同时执行的任务数量。例如,每次只允许 3 个任务并发执行。
示例代码:
import java.util.concurrent.*;public class LimitedConcurrencyExample {private final Semaphore semaphore = new Semaphore(3);private final ExecutorService threadPool = Executors.newFixedThreadPool(3);public void addTask(Runnable task) {threadPool.submit(() -> {try {semaphore.acquire(); // 获取许可task.run();} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {semaphore.release(); // 释放许可}});}public void shutdown() {threadPool.shutdown();}public static void main(String[] args) {LimitedConcurrencyExample example = new LimitedConcurrencyExample();for (int i = 0; i < 10; i++) {example.addTask(() -> {System.out.println("Task executed by " + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟耗时任务} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 程序结束时关闭线程池Runtime.getRuntime().addShutdownHook(new Thread(example::shutdown));}
}
优点:
-
限制并发:通过
Semaphore限制同时执行的任务数量。 -
线程复用:线程池中的线程会复用,减少线程创建和销毁的开销。
4)任务分批处理
如果任务数据量很大,可以将任务分批处理,每批任务由一个线程处理。
示例代码:
import java.util.concurrent.*;public class BatchTaskExample {private final ExecutorService threadPool = Executors.newFixedThreadPool(3);public void processTasks(List<Runnable> tasks) {int batchSize = tasks.size() / 3;for (int i = 0; i < 3; i++) {int start = i * batchSize;int end = (i == 2) ? tasks.size() : start + batchSize;threadPool.submit(() -> {for (int j = start; j < end; j++) {tasks.get(j).run();}});}}public void shutdown() {threadPool.shutdown();}public static void main(String[] args) {BatchTaskExample example = new BatchTaskExample();List<Runnable> tasks = new ArrayList<>();for (int i = 0; i < 10; i++) {tasks.add(() -> {System.out.println("Task executed by " + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟耗时任务} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}example.processTasks(tasks);// 程序结束时关闭线程池Runtime.getRuntime().addShutdownHook(new Thread(example::shutdown));}
}
优点:
-
分批处理:将任务分批处理,避免任务重叠。
-
线程复用:线程池中的线程会复用,减少线程创建和销毁的开销。
总结
根据你的需求,可以选择以下方案:
-
使用线程池:限制线程数量,避免线程过多导致的资源竞争。
-
任务队列:将任务放入队列,按顺序执行,避免任务重叠。
-
限制并发任务数量:通过
Semaphore限制同时执行的任务数量。 -
任务分批处理:将任务分批处理,每批任务由一个线程处理。
选择合适的方案可以有效解决线程过多导致的问题,提高任务执行的效率。
2. 避免函数被重复调用
Java中,可以通过多种方式避免函数被重复调用。以下是常见的方法
1)使用标志位
函数调用前,设置一个标志位表示该函数是否已经被调用过。如果已经被调用多,则不再重复调用。
public class FunctionCall {private static boolean isCalled = false;public static void function() {if (isCalled) {System.out.println("Function is already being called.");return;}isCalled = true;try {// Function logic hereSystem.out.println("Function is being called.");} finally {isCalled = false;}}public static void main(String[] args) {new Thread(() -> function()).start();new Thread(() -> function()).start();}
}
2)使用同步锁
使用同步锁(如 synchronized 或 ReentrantLock)来确保函数在同一时间只能被一个线程调用。
import java.util.concurrent.locks.ReentrantLock;public class FunctionCall {private static final ReentrantLock lock = new ReentrantLock();public static void function() {lock.lock();try {// Function logic hereSystem.out.println("Function is being called.");} finally {lock.unlock();}}public static void main(String[] args) {new Thread(() -> function()).start();new Thread(() -> function()).start();}
}
3)使用AOP和缓存
通过AOP(面向切面编程)和缓存来检测函数是否正在被调用。如果函数正在被调用,则不再重复调用。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager cacheManager() {return new ConcurrentMapCacheManager("functionCache");}
}@Aspect
public class FunctionCallAspect {private final CacheManager cacheManager;public FunctionCallAspect(CacheManager cacheManager) {this.cacheManager = cacheManager;}@Pointcut("execution(* com.example.FunctionCall.function(..))")public void functionCallPointcut() {}@Around("functionCallPointcut()")public Object aroundFunctionCall(ProceedingJoinPoint joinPoint) throws Throwable {Cache cache = cacheManager.getCache("functionCache");if (cache != null && cache.get("functionKey") != null) {System.out.println("Function is already being called.");return null;}cache.put("functionKey", "inProgress");try {return joinPoint.proceed();} finally {cache.evict("functionKey");}}
}public class FunctionCall {public static void function() {// Function logic hereSystem.out.println("Function is being called.");}public static void main(String[] args) {new Thread(() -> function()).start();new Thread(() -> function()).start();}
}
4)使用消息队列
将函数调用请求放入消息队列中,然后由后台服务从队列中取出并处理,避免了函数的重复调用。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class FunctionCall {private static final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();public static void function() {queue.add(() -> {// Function logic hereSystem.out.println("Function is being called.");});}public static void main(String[] args) {new Thread(() -> {while (true) {try {Runnable task = queue.take();task.run();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}).start();new Thread(() -> function()).start();new Thread(() -> function()).start();}
}
5)使用限流算法
通过限流算法(如漏桶算法或令牌桶算法)来控制函数的调用频率,避免函数的重复调用。
import java.util.concurrent.Semaphore;public class FunctionCall {private static final Semaphore semaphore = new Semaphore(1);public static void function() {try {semaphore.acquire();// Function logic hereSystem.out.println("Function is being called.");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {semaphore.release();}}public static void main(String[] args) {new Thread(() -> function()).start();new Thread(() -> function()).start();}
}
持续更新ing,推荐博客:
- Java 面试指南 | JavaGuide
相关文章:
线程配置经验
工作时,时常会遇到,线程相关的问题与解法,本人会持续对开发过程中遇到的关于线程相关的问题及解决记录更新记录在此篇博客中。 目录 一、线程基本知识 1. 线程和进程 二、问题与解法 1. 避免乘法级别数量线程并行 1)使用线程池…...
动态规划DP 数字三角形模型 传纸条(题目分析+C++完整代码)
传纸条 原题链接 AcWing 275. 传纸条 题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。 一次素质拓展活动中,班上同学安排坐成一个 m行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此&#x…...
Ubuntu二进制部署K8S 1.29.2
本机说明 本版本非高可用,单Master,以及一个Node 新装的 ubuntu 22.04k8s 1.29.3使用该文档请使用批量替换 192.168.44.141这个IP,其余照着复制粘贴就可以成功需要手动 设置一个 固定DNS,我这里设置的是 8.8.8.8不然coredns无法…...
第05章 10 地形梯度场模拟显示
在 VTK(Visualization Toolkit)中,可以通过计算地形数据的梯度场,并用箭头或线条来表示梯度方向和大小,从而模拟显示地形梯度场。以下是一个示例代码,展示了如何使用 VTK 和 C 来计算和显示地形数据的梯度场…...
全程Kali linux---CTFshow misc入门
图片篇(基础操作) 第一题: ctfshow{22f1fb91fc4169f1c9411ce632a0ed8d} 第二题 解压完成后看到PNG,可以知道这是一张图片,使用mv命令或者直接右键重命名,修改扩展名为“PNG”即可得到flag。 ctfshow{6f66202f21ad22a2a19520cdd…...
[ Spring ] Spring Cloud Alibaba Message Stream Binder for RocketMQ 2025
文章目录 IntroduceProject StructureDeclare Plugins and ModulesApply Plugins and Add DependenciesSender PropertiesSender ApplicationSender ControllerReceiver PropertiesReceiver ApplicationReceiver Message HandlerCongratulationsAutomatically Send Message By …...
深度学习笔记——循环神经网络之LSTM
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍面试过程中可能遇到的循环神经网络LSTM知识点。 文章目录 文本特征提取的方法1. 基础方法1.1 词袋模型(Bag of Words, BOW)工作…...
AI 模型评估与质量控制:生成内容的评估与问题防护
在生成式 AI 应用中,模型生成的内容质量直接影响用户体验。然而,生成式模型存在一定风险,如幻觉(Hallucination)问题——生成不准确或完全虚构的内容。因此,在构建生成式 AI 应用时,模型评估与质…...
[MILP] Logical Constraints 0-1 (Note2)
1. 如果选择了项目1,则项目2,3也要求被选中 表示为: 2. 如果确定了选项目1,则接下来必须选项目2或者项目3 表示为: or 3. 如果同时选择了项目2和项目3,则不可以选择项目1 表示为: 4. 如果…...
DFFormer实战:使用DFFormer实现图像分类任务(二)
文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度,DP多卡,EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…...
蓝桥杯例题四
每个人都有无限潜能,只要你敢于去追求,你就能超越自己,实现梦想。人生的道路上会有困难和挑战,但这些都是成长的机会。不要被过去的失败所束缚,要相信自己的能力,坚持不懈地努力奋斗。成功需要付出汗水和努…...
如何复现o1模型,打造医疗 o1?
如何复现o1模型,打造医疗 o1? o1 树搜索一、起点:预训练规模触顶与「推理阶段(Test-Time)扩展」的动机二、Test-Time 扩展的核心思路与常见手段1. Proposer & Verifier 统一视角方法1:纯 Inference Sca…...
PostgreSQL TRUNCATE TABLE 操作详解
PostgreSQL TRUNCATE TABLE 操作详解 引言 在数据库管理中,经常需要对表进行操作以保持数据的有效性和一致性。TRUNCATE TABLE 是 PostgreSQL 中一种高效删除表内所有记录的方法。本文将详细探讨 PostgreSQL 中 TRUNCATE TABLE 的使用方法、性能优势以及注意事项。 什么是 …...
【JavaWeb06】Tomcat基础入门:架构理解与基本配置指南
文章目录 🌍一. WEB 开发❄️1. 介绍 ❄️2. BS 与 CS 开发介绍 ❄️3. JavaWeb 服务软件 🌍二. Tomcat❄️1. Tomcat 下载和安装 ❄️2. Tomcat 启动 ❄️3. Tomcat 启动故障排除 ❄️4. Tomcat 服务中部署 WEB 应用 ❄️5. 浏览器访问 Web 服务过程详…...
【NOI】C++程序结构入门之循环结构三-计数求和
文章目录 前言一、计数求和1.导入2.计数器3.累加器 二、例题讲解问题:1741 - 求出1~n中满足条件的数的个数和总和?问题:1002. 编程求解123...n问题:1004. 编程求1 * 2 * 3*...*n问题:1014. 编程求11/21/3...1/n问题&am…...
[Linux]Shell脚本中以指定用户运行命令
前言 当我们为Linux设置了用户自启动的shel脚本,默认会使用root用户执行启动脚本中的命令,那么我们如何在启动脚本中切换为指定用户指定命令呢。 命令 以下将列出两条命令,两条命令都可以实现以指定用户运行命令,凭喜好选择使用…...
通过 NAudio 控制电脑操作系统音量
根据您的需求,以下是通过 NAudio 获取和控制电脑操作系统音量的方法: 一、获取和控制系统音量 (一)获取系统音量和静音状态 您可以使用 NAudio.CoreAudioApi.MMDeviceEnumerator 来获取系统默认音频设备的音量和静音状态&#…...
新项目上传gitlab
Git global setup git config --global user.name “FUFANGYU” git config --global user.email “fyfucnic.cn” Create a new repository git clone gitgit.dev.arp.cn:casDs/sawrd.git cd sawrd touch README.md git add README.md git commit -m “add README” git push…...
【异步编程基础】FutureTask基本原理与异步阻塞问题
文章目录 一、FutureTask 的桥梁作用二、Future 模式与异步回调三、 FutureTask获取异步结果的逻辑1. 获取异步执行结果的步骤2. 举例说明3. FutureTask的异步阻塞问题 Runnable 用于定义无返回值的任务,而 Callable 用于定义有返回值的任务。然而,Calla…...
原生 Node 开发 Web 服务器
一、创建基本的 HTTP 服务器 使用 http 模块创建 Web 服务器 const http require("http");// 创建服务器const server http.createServer((req, res) > {// 设置响应头res.writeHead(200, { "Content-Type": "text/plain" });// 发送响应…...
LeetCode热题100(一)—— 1.两数之和
LeetCode热题100(一)—— 1.两数之和 题目描述代码实现思路解析 你好,我是杨十一,一名热爱健身的程序员在Coding的征程中,不断探索与成长LeetCode热题100——刷题记录(不定期更新) 此系列文章用…...
二叉树高频题目——下——不含树型dp
一,普通二叉树上寻找两个节点的最近的公共祖先 1,介绍 LCA(Lowest Common Ancestor,最近公共祖先)是二叉树中经常讨论的一个问题。给定二叉树中的两个节点,它的LCA是指这两个节点的最低(最深&…...
vue事件总线(原理、优缺点)
目录 一、原理二、使用方法三、优缺点优点缺点 四、使用注意事项具体代码参考: 一、原理 在Vue中,事件总线(Event Bus)是一种可实现任意组件间通信的通信方式。 要实现这个功能必须满足两点要求: (1&#…...
PyCharm介绍
PyCharm的官网是https://www.jetbrains.com/pycharm/。 以下是在PyCharm官网下载和安装软件的步骤: 下载步骤 打开浏览器,访问PyCharm的官网https://www.jetbrains.com/pycharm/。在官网首页,点击“Download”按钮进入下载页面。选择适合自…...
《CPython Internals》读后感
一、 为什么选择这本书? Python 是本人工作中最常用的开发语言,为了加深对 Python 的理解,更好的掌握 Python 这门语言,所以想对 Python 解释器有所了解,看看是怎么使用C语言来实现Python的,以期达到对 Py…...
音频入门(一):音频基础知识与分类的基本流程
音频信号和图像信号在做分类时的基本流程类似,区别就在于预处理部分存在不同;本文简单介绍了下音频处理的方法,以及利用深度学习模型分类的基本流程。 目录 一、音频信号简介 1. 什么是音频信号 2. 音频信号长什么样 二、音频的深度学习分…...
Redis --- 分布式锁的使用
我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法,但是这样不能让锁跨JVM也就是跨进程去使用,只能适用在单体项目中如下图: 为了解决这种场景,我们就需要用一个锁监视器对全部集群进行监视…...
用C++编写一个2048的小游戏
以下是一个简单的2048游戏的实现。这个实现使用了控制台输入和输出,适合在终端或命令行环境中运行。 2048游戏的实现 1.游戏逻辑 2048游戏的核心逻辑包括: • 初始化一个4x4的网格。 • 随机生成2或4。 • 处理玩家的移动操作(上、下、左、…...
【设计模式-行为型】状态模式
一、什么是状态模式 什么是状态模式呢,这里我举一个例子来说明,在自动挡汽车中,挡位的切换是根据驾驶条件(如车速、油门踏板位置、刹车状态等)自动完成的。这种自动切换挡位的过程可以很好地用状态模式来描述。状态模式…...
CentOS/Linux Python 2.7 离线安装 Requests 库解决离线安装问题。
root@mwcollector1 externalscripts]# cat /etc/os-release NAME=“Kylin Linux Advanced Server” VERSION=“V10 (Sword)” ID=“kylin” VERSION_ID=“V10” PRETTY_NAME=“Kylin Linux Advanced Server V10 (Sword)” ANSI_COLOR=“0;31” 这是我系统的版本,由于是公司内网…...
