Java 中的 volatile和synchronized和 ReentrantLock区别讲解和案例示范
在 Java 的并发编程中,volatile
、synchronized
和 ReentrantLock
是三种常用的同步机制。每种机制都有其独特的特性、优缺点和适用场景。理解它们之间的区别以及在何种情况下使用哪种机制,对提高程序的性能和可靠性至关重要。本文将详细探讨这三种机制的特性、使用场景及示例。
1. volatile
的特性
1.1 保证可见性
volatile
修饰的变量确保所有线程都能看到变量的最新值,避免了线程间的缓存不一致问题。
示例
public class VolatileVisibility {private volatile boolean running = true;public void stop() {running = false; // 修改 running}public void execute() {while (running) {// 执行任务}}
}
在这个例子中,running
变量被声明为 volatile
,当一个线程调用 stop()
方法时,其他线程会立即看到 running
的值为 false
。
1.2 禁止指令重排序
volatile
还保证了对该变量的操作不会被重排序,从而确保程序执行的顺序性。
示例
public class VolatileReordering {private int a = 0;private volatile int b = 0;public void method1() {a = 1; // 1b = 2; // 2}public void method2() {if (b == 2) { // 3System.out.println(a); // 4}}
}
如果没有 volatile
,可能会发生重排序,使得 method2()
在 method1()
的 b = 2
之前执行,导致 a
的值可能为 0。
1.3 不保证原子性
volatile
不能保证对复合操作的原子性,比如自增操作。
示例
public class VolatileAtomicity {private volatile int count = 0;public void increment() {count++; // 非原子操作}public int getCount() {return count;}
}
在这个例子中,increment()
方法对 count
的自增不是原子操作,可能导致数据不一致。
2. synchronized
的特性
2.1 可重入性
synchronized
允许同一线程多次获得同一把锁,而不会发生死锁。
示例
public class SynchronizedReentrancy {public synchronized void method1() {method2(); // 允许重入}public synchronized void method2() {// 执行任务}
}
在这个例子中,method1()
可以安全地调用 method2()
,因为 synchronized
允许可重入。
2.2 不可中断性
synchronized
的获取锁是不可中断的,线程在等待锁时不能被中断。
示例
public class SynchronizedInterruptibility {public synchronized void lockedMethod() throws InterruptedException {Thread.sleep(10000); // 模拟长时间执行}public void execute() {Thread thread = new Thread(() -> {try {lockedMethod();} catch (InterruptedException e) {// 处理被中断}});thread.start();thread.interrupt(); // 线程在等待锁时被中断}
}
在这个例子中,lockedMethod()
无法被中断,导致线程无法释放锁。
2.3 锁的升级和降级
synchronized
支持锁的升级和降级。在方法中直接使用 synchronized
,在代码块中也可以使用。
示例
public class SynchronizedUpgrade {public synchronized void method() {// 持有对象锁synchronized (this) {// 持有同一把锁}}
}
在这个例子中,使用了对象的锁和类的锁,展示了锁的升级和降级。
2.4 不公平性
synchronized
不保证公平性,可能导致某些线程长时间等待。
示例
public class SynchronizedFairness {public synchronized void method() {// 执行任务}
}
在这个例子中,多个线程访问 method()
时,无法保证先请求的线程先获得锁。
2.5 可见性、原子性和有序性
synchronized
保证了对共享变量的可见性、原子性和有序性。
示例
public class SynchronizedVisibility {private int data;public synchronized void updateData(int value) {data = value; // 更新数据}public synchronized int readData() {return data; // 读取数据}
}
在这个例子中,updateData()
和 readData()
方法保证了 data
的线程安全。
3. ReentrantLock
的特性
3.1 可重入性
ReentrantLock
允许同一线程多次获得锁,支持可重入。
示例
public class ReentrantLockReentrancy {private final ReentrantLock lock = new ReentrantLock();public void method() {lock.lock();try {method(); // 允许重入} finally {lock.unlock();}}
}
在这个例子中,method()
方法可以安全地调用自身,因为 ReentrantLock
允许可重入。
3.2 可中断性
ReentrantLock
允许在等待锁时被中断,提供了更好的控制。
示例
public class ReentrantLockInterruptibility {private final ReentrantLock lock = new ReentrantLock();public void lockedMethod() throws InterruptedException {lock.lockInterruptibly(); // 可中断的锁try {// 执行任务} finally {lock.unlock();}}
}
在这个例子中,lockedMethod()
可以被中断,提供了更好的控制。
3.3 公平性和非公平性
ReentrantLock
支持公平和非公平锁的选择。
示例
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
ReentrantLock unfairLock = new ReentrantLock(); // 非公平锁
在这个例子中,公平锁会按照线程请求的顺序来获取锁,而非公平锁则可能导致某些线程饥饿。
3.4 条件变量
ReentrantLock
提供了条件变量支持,可以实现复杂的线程间协作。
示例
public class ReentrantLockCondition {private final ReentrantLock lock = new ReentrantLock();private final Condition condition = lock.newCondition();public void await() throws InterruptedException {lock.lock();try {condition.await(); // 等待条件} finally {lock.unlock();}}public void signal() {lock.lock();try {condition.signal(); // 唤醒等待的线程} finally {lock.unlock();}}
}
在这个例子中,使用条件变量实现了线程间的协作。
4. 三者之间的区别
特性 | volatile | synchronized | ReentrantLock |
---|---|---|---|
可见性 | 保证可见性 | 保证可见性 | 保证可见性 |
互斥性 | 不保证 | 保证互斥性 | 保证互斥性 |
是否可重入 | 不适用 | 支持可重入 | 支持可重入 |
代码块范围 | 只能用于变量 | 代码块或方法 | 代码块或方法 |
锁的获取方式 | 无 | 自动获取 | 显式获取 |
公平性 | 无 | 无 | 支持公平性 |
性能 | 性能开销小 | 性能开销中等 | 性能开销较大 |
5. 适用场景分析
5.1 何时使用 volatile
适用场景:当需要保证某个变量的可见性,但不需要互斥访问时,使用 volatile
是最佳选择。
示例
public class VolatileFlag {private volatile boolean flag = true;public void stop() {flag = false;}public void run() {while (flag) {// 执行任务}}
}
在这个场景中,volatile
可以有效地减少上下文切换,提高性能。
5.2 何时使用 synchronized
适用场景:当需要对共享资源进行互斥访问时,使用 synchronized
是最佳选择。
示例
public class SynchronizedCounter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
在这个例子中,synchronized
确保了 count
的线程安全。
5.3 何时使用 ReentrantLock
适用场景:当需要更加灵活的锁定机制,比如可重入性、公平性或可中断的锁时,使用 ReentrantLock
是最佳选择。
示例
public class ReentrantLockCounter {private final ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}
在这个例子中,ReentrantLock
允许灵活的控制锁的获取和释放。
6. 总结
通过本文对 volatile
、synchronized
和 ReentrantLock
的深入分析,读者可以了解到它们各自的特性、优缺点及适用场景。在并发编程中,选择合适的同步机制不仅可以提高程序性能,还能有效地避免潜在的线程安全问题。
在实际开发中,根据不同的需求和场景,合理使用这三种机制,可以使得 Java 程序在并发执行时更加高效和安全。
相关文章:
Java 中的 volatile和synchronized和 ReentrantLock区别讲解和案例示范
在 Java 的并发编程中,volatile、synchronized 和 ReentrantLock 是三种常用的同步机制。每种机制都有其独特的特性、优缺点和适用场景。理解它们之间的区别以及在何种情况下使用哪种机制,对提高程序的性能和可靠性至关重要。本文将详细探讨这三种机制的…...
从GDAL中 读取遥感影像的信息
从GDAL提供的实用程序来看,很多程序的后缀都是 .py ,这充分地说明了Python语言在GDAL的开发中得到了广泛的应用。 1. 打开已有的GeoTIF文件 下面我们试着读取一个GeoTiff文件的信息。第一步就是打开一个数据集。 >>> from osgeo import gdal…...

算法闭关修炼百题计划(一)
多看优秀的代码一定没有错,此篇博客属于个人学习记录 1.两数之和2.前k个高频元素3.只出现一次的数字4.数组的度5.最佳观光组合6.整数反转7.缺失的第一个正数8.字符串中最多数目的子序列9.k个一组翻转链表10.反转链表II11. 公司命名12.合并区间13.快速排序14.数字中的…...

vue3实现打字机的效果,可以换行
之前看了很多文章,效果是实现了,就是没有自动换行的效果,参考了文章写了一个,先上个效果图,卡顿是因为模仿了卡顿的效果,还是很丝滑的 目录 效果图:代码如下 效果图: 
【如何学习操作系统】——学会学习的艺术
🐟作者简介:一名大三在校生,喜欢编程🪴 🐡🐙个人主页🥇:Aic山鱼 🐠WeChat:z7010cyy 🦈系列专栏:🏞️ 前端-JS基础专栏✨前…...

stm32 flash无法擦除
通过bushound调试代码发现,当上位机发送命令到模组后flash将不能擦除,通过 HAL_FLASH_GetError()函数查找原因是FLASH Programming Sequence error(编程顺序错误),解决办法是在解锁后清零标志位…...
Android—ANR日志分析
获取ANR日志: ANR路径:/data/anrADB指令:adb bugreport D:\bugrep.zip ANR日志分析步骤: “main” prio:主线程状态beginning of crash:搜索 crash 相关信息CPU usage from:搜索 cpu 使用信息…...

9.29 LeetCode 3304、3300、3301
思路: ⭐进行无限次操作,但是 k 的取值小于 500 ,所以当 word 的长度大于 500 时就可以停止操作进行取值了 如果字符为 ‘z’ ,单独处理使其变为 ‘a’ 得到得到操作后的新字符串,和原字符串拼接 class Solution { …...

近万字深入讲解iOS常见锁及线程安全
什么是锁? 在程序中,当多个任务(或线程)同时访问同一个资源时,比如多个操作同时修改一份数据,可能会导致数据不一致。这时候,我们需要“锁”来确保同一时间只有一个任务能够操作这个数据&#…...
linux创建固定大小的文件夹用于测试
在linux上创建固定大小的文件夹用于测试磁盘空间不足时的应用故障。 实验环境为centos7,有两种简易方法: 一、使用ramdisk 1、创建文件夹 mkdir /var/mytest 2、创建一个1m大小的临时文件 mount none /var/mytest -t tmpfs -o size1m size也可以写…...

大模型学习路线:这会是你见过最全最新的大模型学习路线【2024最新】
大模型学习路线 建议先从主流的Llama开始,然后选用中文的Qwen/Baichuan/ChatGLM,先快速上手体验prompt工程,然后再学习其架构,跑微调脚本 如果要深入学习,建议再按以下步骤,从更基础的GPT和BERT学起&…...

了解云计算工作负载保护的重要性,确保数据和应用程序安全
云计算de小白 云计算技术的快速发展使数据和应用程序安全成为一种关键需求,而不仅仅是一种偏好。随着越来越多的客户公司将业务迁移到云端,保护他们的云工作负载(指所有部署的应用程序和服务)变得越来越重要。云工作负载保护&…...
Swagger3基本使用
Swagger 课程目标 Swagger简介【了解】 Springboot整合swagger【掌握】 Swagger 常用注解【掌握】 knife4j-Swagger【会用】 一、Swagger3简介 Swagger 是一系列 RESTful API 的工具,通过 Swagger 可以获得项目的⼀种交互式文档,客户端 SDK 的自 动…...

如何借助Java批量操作Excel文件?
最新技术资源(建议收藏) https://www.grapecity.com.cn/resources/ 前言 | 问题背景 在操作Excel的场景中,通常会有一些针对Excel的批量操作,批量的意思一般有两种: 对批量的Excel文件进行操作。如导入多个Excel文件…...
JUC并发编程_Lock锁
JUC并发编程_Lock锁 1、Lock锁介绍2、主要方法3、与 synchronized 的区别4、Condition 使用示例 1、Lock锁介绍 Java中的 Lock 锁是 java.util.concurrent.locks 包下的一个接口,它提供了比 synchronized 关键字更灵活的锁定机制。 2、主要方法 lock():…...

Unity中的功能解释(数学位置相关和事件)
向量计算 Vector3.Slerp(起点坐标,终点坐标,t),可是从起点坐标以一个圆形轨迹到终点坐标,有那么多条轨迹,那怎么办 Vector3.Slerp 进行的是沿球面插值,因此并不是沿着严格的“圆形…...
ElementPlus---Timeline 时间线组件使用示例
介绍 使用ElementPlus时间线组件在后台首页实现通知公告列表展示,使用Vue3开发。 实现代码 Vue3代码 <el-timeline><el-timeline-itemstyle"max-width: 600px"v-for"(activity, index) in activities":key"index":times…...

推荐4款2024年大家都在用的高质量翻译器。
翻译器在我们的生活中有着很重要的作用,不管是我们在学习还是工作,生活娱乐,出国旅游等场合都会派上用场,它是我们解决沟通的障碍,提高阅读效率的好帮手。我自己使用的翻译器有很多,可以给大家列举几款特别…...

Mybatis 返回 Map 对象
一、场景介绍 假设有如下一张学生表: CREATE TABLE student (id int NOT NULL AUTO_INCREMENT COMMENT 主键,name varchar(100) NOT NULL COMMENT 姓名,gender varchar(10) NOT NULL COMMENT 性别,grade int NOT NULL COMMENT 年级,PRIMARY KEY (id) ) ENGINEInnoD…...

Vue3(三)路由基本使用、工作模式(history,hash)、query传参和param传参、props配置、编程式路由导航
文章目录 一、路由的基本使用二、路由器的工作模式三、RouterLink中to的两种写法四、嵌套路由五、路由传参1. query传参2. params传参 六、路由的propos配置七、编程式路由导航 一、路由的基本使用 安装:npm i vue-router 在src/pages文件下,创建三个路…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...