线程配置经验
工作时,时常会遇到,线程相关的问题与解法,本人会持续对开发过程中遇到的关于线程相关的问题及解决记录更新记录在此篇博客中。
目录
一、线程基本知识
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)使用线程池…...
火语言RPA--KimiAiFree服务
🚩【组件功能】:KimiAiFree服务支持联网搜索、支持智能体对话、支持长文档解读、支持图像OCR。 配置预览 配置说明 服务地址 支持T或# 自行搭建或第三方提供的KimiAiFree服务地址。 RefreshToken 支持T或# 与KimiAiFree服务交互时用到的token。从ki…...
P6120 [USACO17JAN] Hoof, Paper, Scissor S
难度:普及/提高−; 题意: 石头、剪刀、布游戏,先给出 n n n 轮已经知道的其中一人的对局情况,例如样例: 5 P - 布 P - 布 H - 石头 P - 布 S - 剪刀另外一人,只允许修改一次机会的情况下…...
Android Studio打包APK
1.导出APK安装包 如果是首次打包,Create new 单击蓝色对话框右边文件夹📂图标 ,选择密钥保存路径,然后在下方File name对话框中填写您想要名称,再点击OK回到密钥创建对话框。 在此对话框中填写密码(Passwo…...
08 比特币通用技术介绍
比特币分层 比特币区块结构 存储结构 区块是比特币存储交易的结构,一个区块总是指向其父节点。 一个区块包含三个字段:区块头、区块交易数量、交易列表。交易数量受到区块大小限制,输入、输出数量和脚本都会占用区块空间,矿工往…...
拟合损失函数
文章目录 拟合损失函数一、线性拟合1.1 介绍1.2 代码可视化1.2.1 生成示例数据1.2.2 损失函数1.2.3 绘制三维图像1.2.4 绘制等高线1.2.5 损失函数关于斜率的函数 二、 多变量拟合2.1 介绍2.2 代码可视化2.2.1 生成示例数据2.2.2 损失函数2.2.3 绘制等高线 三、 多项式拟合3.1 介…...
二进制安卓清单 binary AndroidManifest - XCTF apk 逆向-2
XCTF 的 apk 逆向-2 题目 wp,这是一道反编译对抗题。 题目背景 AndroidManifest.xml 在开发时是文本 xml,在编译时会被 aapt 编译打包成为 binary xml。具体的格式可以参考稀土掘金 MindMac 做的类图(2014),下面的博…...
在线免费快速无痕去除照片海报中的文字logo
上期和大家分享了用photoshop快速无痕去除照片海报中的文字logo的方法,有的同学觉得安装PS太麻烦,有那下载安装时间早都日落西山了,问有没有合适的在线方法可以快速去除;达芬奇上网也尝试了几个网站,今天分享一个对国人…...
引领未来科技潮流:Web3 前沿发展趋势
随着技术不断发展,我们正站在一个全新的互联网时代的门槛上,Web3的出现正在重新定义互联网的构架和运作方式。Web3,作为互联网的下一代发展趋势,其核心思想是去中心化、开放与用户主权。与现有的Web2.0相比,Web3更加注…...
【番外篇】鸿蒙扫雷天纪:运混沌灵智勘破雷劫天局
大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 这一节课我们不学习新的知识,我们来做一个扫雷小游戏 目录 扫雷小游戏概述一、扫雷游戏分析…...
08.OSPF 特殊区域及其他特性
OSPF 特殊区域及其他特性 一. 前言OSPF的四个特殊区域Stub末梢区域Totally Stub完全末梢区域NSSATotally NSSA完全的NSSA二.Stub 区域和 Totally Stub 区域(1)网络规模变大引发的问题(2)传输区域和末端区域(3)Stub 区域(4)Totally Stub 区域三.NSSA 区域和 Totally NSS…...
人工智能在医疗领域的应用有哪些?
人工智能在医疗领域的应用十分广泛,涵盖了诊断、治疗、药物研发等多个环节,以下是一些主要的应用: 医疗影像诊断 疾病识别:通过分析 X 光、CT、MRI 等影像,人工智能算法能够识别出肿瘤、结节、骨折等病变,…...
c#使用Confluent.Kafka实现生产者发送消息至kafka(远程连接kafka发送消息超时的解决 Local:Message timed out)
水一篇: 参考:c#使用Confluent.Kafka实现生产者发送消息至kafka(远程连接kafka发送消息超时的解决 Local:Message timed out) - 寒冰之光 - 博客园 该死的Kafka,远程连接Kafka超时以及解决办法 - 博客王大…...
【2025年数学建模美赛F题】(顶刊论文绘图)模型代码+论文
全球网络犯罪与网络安全政策的多维度分析及效能评估 摘要1 Introduction1.1 Problem Background1.2Restatement of the Problem1.3 Literature Review1.4 Our Work 2 Assumptions and Justifications数据完整性与可靠性假设:法律政策独立性假设:人口统计…...
DeepSeek 的背景介绍
在全球人工智能大模型蓬勃发展的浪潮中,DeepSeek 宛如一颗耀眼的新星,迅速崛起并吸引了众多关注的目光。它的出现不仅为人工智能领域注入了新的活力,也在一定程度上改变了行业的竞争格局。 一、创立背景与资金支持 DeepSeek,中文…...
Meta 计划 2025 年投资 650 亿美元推动 AI 发展
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
信息学奥赛一本通 2110:【例5.1】素数环
【题目链接】 ybt 2110:【例5.1】素数环 【题目考点】 1. 深搜回溯 2. 质数 【解题思路】 1~n的数字构成一个环,要求相邻数字加和必须是质数。 该题最终输出的是一个序列,只不过逻辑上序列最后一个数字的下一个数字就是序列的第一个数字…...
Redis、MongoDB 和 MySQL评估
Redis、MongoDB 和 MySQL 是三种不同类型的数据库系统,各自有独特的特点和适用场景。MySQL 是一个关系型数据库管理系统(RDBMS),而 Redis 和 MongoDB 是非关系型数据库(NoSQL)。以下是对这三者的比较以及它…...
P1719 最大加权矩形
为了更好的备战 NOIP2013,电脑组的几个女孩子 LYQ,ZSC,ZHQ 认为,我们不光需要机房,我们还需要运动,于是就决定找校长申请一块电脑组的课余运动场地,听说她们都是电脑组的高手,校长没有马上答应他们…...
在生产环境中部署和管理 Apache:运维从入门到精通
在生产环境中部署和管理 Apache:运维从入门到精通 引言 Apache HTTP Server(简称 Apache)作为世界上最受欢迎的 Web 服务器之一,因其稳定性、灵活性和丰富的模块支持而被广泛使用。从个人网站到企业级应用,Apache 都能游刃有余。然而,要想在生产环境中高效部署和管理 A…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
