线程同步:确保多线程程序的安全与高效!
全文目录:
- 开篇语
- 前序
- 前言
- 第一部分:线程同步的概念与问题
- 1.1 线程同步的概念
- 1.2 线程同步的问题
- 1.3 线程同步的解决方案
- 第二部分:`synchronized`关键字的使用
- 2.1 使用` synchronized`修饰方法
- 2.2 使用` synchronized`修饰代码块
- 第三部分:`ReentrantLock`与条件变量
- 3.1 `ReentrantLock`的使用
- 3.2 条件变量:`Condition`
- 第四部分:死锁的检测与预防
- 4.1 死锁的概念
- 4.2 死锁的预防
- 总结
- 文末
开篇语
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前序
在多线程编程中,线程同步是确保多个线程在访问共享资源时不会出现竞争问题的关键。线程同步保证了线程之间的协调与数据的一致性,避免了常见的线程安全问题,例如脏数据和竞态条件。随着现代计算机处理能力的提升,多线程编程已经成为开发高效程序的重要技巧。
今天,我们将深入探讨线程同步的基本概念、synchronized
关键字的使用、ReentrantLock
与条件变量的应用,以及如何检测与预防死锁问题。
前言
在多线程编程中,多个线程可能会同时访问共享资源,如果不加以控制,可能会导致数据的不一致性。例如,一个线程正在修改某个共享变量,另一个线程可能会在这个变量还没完全更新时读取它,导致错误的结果。为了解决这些问题,我们需要使用线程同步技术来确保只有一个线程能够访问共享资源。
今天,我们将通过多个实例深入了解线程同步的概念和工具,帮助你写出更安全、高效的多线程代码。
第一部分:线程同步的概念与问题
1.1 线程同步的概念
线程同步指的是在多线程环境中,确保多个线程在执行过程中能够合理、协调地访问共享资源,从而避免出现线程安全问题。线程同步的目标是确保同一时刻只有一个线程能够访问某个共享资源,这样可以防止数据竞争、死锁等问题。
1.2 线程同步的问题
-
竞态条件(Race Condition):当两个或多个线程尝试同时访问共享资源,且操作顺序没有得到妥善控制时,就会出现竞态条件,可能导致数据的不一致。
-
脏数据(Dirty Data):如果一个线程正在修改共享数据,另一个线程读取时没有得到正确的值,就可能读取到脏数据。
-
死锁(Deadlock):多个线程因相互等待对方持有的资源而进入无限等待的状态,导致程序无法继续执行。
1.3 线程同步的解决方案
为了解决上述问题,我们可以使用不同的线程同步机制,例如:synchronized
关键字、ReentrantLock
、Condition
等。这些机制能够确保在同一时刻只有一个线程能够访问共享资源,从而保证数据的一致性。
第二部分:synchronized
关键字的使用
synchronized
是Java提供的最基础的线程同步工具,它可以修饰方法或代码块,确保同一时刻只有一个线程能够执行被修饰的部分。
2.1 使用 synchronized
修饰方法
当一个方法被 synchronized
修饰时,表示该方法在执行时会获得该方法所属对象的锁。在多线程环境下,其他线程必须等待当前线程释放锁后才能进入该方法。
示例:
public class SynchronizedExample {private int count = 0;// 使用synchronized修饰方法public synchronized void increment() {count++;}public static void main(String[] args) {SynchronizedExample example = new SynchronizedExample();// 创建多个线程Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: " + example.count); // 输出结果应为2000}
}
解释:
- 在上面的例子中,
increment()
方法被synchronized
修饰,确保在任何时刻只有一个线程可以修改count
的值,避免了竞态条件。
2.2 使用 synchronized
修饰代码块
如果只需要同步方法中的一部分代码,可以使用synchronized
修饰代码块。synchronized
代码块的锁是对象锁,而不是方法锁。
示例:
public class SynchronizedBlockExample {private int count = 0;public void increment() {synchronized (this) { // 锁住当前对象count++;}}public static void main(String[] args) {SynchronizedBlockExample example = new SynchronizedBlockExample();// 创建多个线程Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: " + example.count); // 输出结果应为2000}
}
解释:
synchronized
代码块通过锁住this
对象,保证只有一个线程能够进入increment()
方法中的代码块,避免并发问题。
第三部分:ReentrantLock
与条件变量
除了synchronized
,Java还提供了更灵活的锁机制——ReentrantLock
,它比synchronized
提供了更多的功能,特别是在高并发情况下能够提高性能。
3.1 ReentrantLock
的使用
ReentrantLock
是java.util.concurrent
包下的一个锁类,允许显式地获取和释放锁。与synchronized
不同,ReentrantLock
可以尝试非阻塞式获取锁、可以中断获取锁的线程,还能通过tryLock()
方法进行更细粒度的控制。
示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock(); // 获取锁try {count++;} finally {lock.unlock(); // 释放锁}}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();// 创建多个线程Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: " + example.count); // 输出结果应为2000}
}
解释:
ReentrantLock
可以精确控制锁的获取和释放,相比synchronized
,它提供了更好的灵活性和性能。
3.2 条件变量:Condition
Condition
接口与Object
的wait()
和notify()
类似,但提供了更强大的功能。它通常与ReentrantLock
一起使用,可以让线程在某些条件满足时被唤醒。
示例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ConditionExample {private final Lock lock = new ReentrantLock();private final Condition condition = lock.newCondition();public void produce() throws InterruptedException {lock.lock();try {System.out.println("Producing...");condition.await(); // 等待System.out.println("Produced!");} finally {lock.unlock();}}public void consume() throws InterruptedException {lock.lock();try {Thread.sleep(1000);System.out.println("Consuming...");condition.signal(); // 唤醒等待的线程} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {ConditionExample example = new ConditionExample();Thread producer = new Thread(() -> {try {example.produce();} catch (InterruptedException e) {e.printStackTrace();}});Thread consumer = new Thread(() -> {try {example.consume();} catch (InterruptedException e) {e.printStackTrace();}});producer.start();consumer.start();}
}
解释:
Condition
提供了比wait()
和notify()
更强大的功能,可以在多线程程序中实现更复杂的同步机制。
第四部分:死锁的检测与预防
4.1 死锁的概念
死锁是指两个或多个线程在执行过程中,由于争夺资源而造成一种互相等待的现象,导致程序无法继续执行。
死锁发生的条件:
- 互斥条件:每个资源只有一个线程可以使用。
- 占有并等待:一个线程占有了某些资源,但在等待其他资源时不释放自己已经占有的资源。
- 非抢占条件:资源不能被其他线程强制抢占。
- 循环等待:多个线程形成一种环形的等待关系。
4.2 死锁的预防
死锁的预防可以通过以下几种方式:
- 避免循环等待:确保线程请求资源的顺序一致。
- 避免占有并等待:线程在请求资源时,不持有任何资源。
- 使用
tryLock()
:ReentrantLock
的tryLock()
方法可以避免线程死锁。
总结
线程同步是多线程编程中的核心内容,掌握不同的同步机制,能帮助我们避免竞态条件、脏数据和死锁等问题。通过使用synchronized
关键字、ReentrantLock
、Condition
等同步工具,我们可以有效地控制线程对共享资源的访问,从而提高程序的安全性和性能。
了解并正确应用这些工具,让你能够编写高效、健壮的并发程序,避免常见的并发问题。在多线程编程中,线程同步不仅是确保程序正常运行的基础,也是提升程序稳定性的关键因素。
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!
相关文章:
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...