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文件下,创建三个路…...

TypeScript概念讲解
简单来说,TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准; TypeScript 由微软开发的自由和开源的编程语言; TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 Jav…...

C++ | Leetcode C++题解之第437题路径总和III
题目: 题解: class Solution { public:unordered_map<long long, int> prefix;int dfs(TreeNode *root, long long curr, int targetSum) {if (!root) {return 0;}int ret 0;curr root->val;if (prefix.count(curr - targetSum)) {ret pref…...

回复《对话损友 2》
回复《对话损友 2》 承蒙贵人挂念,亦感激贵人给予这般交流的契机(对话损友 2 -- 回复-CSDN博客)。我自身也一直期望能留存些岁月的痕迹,然而却常困惑于不知哪些事物值得铭记,哪些又应被永远忘却。 随着时光流转&#x…...

MySQL - 运维篇
一、日志 1. 错误日志 2. 二进制日志 3. 查询日志 记录了所有的增删改查语句以及DDL语句 4. 慢查询日志 二、主从复制 1. 概述 2. 原理 3. 搭建 三、分库分表 1. 介绍 2. Mycat概述 3. Mycat入门 4. Mycat配置 5. Mycat分片 6. Mycat管理及监控 四、读写分离 1. 介绍 2. 一…...

WebGIS开发及市面上各种二三维GIS开发框架对比分析
GIS前端开发是现代WebGIS应用开发中非常重要的一环,通过前端开发框架,可以实现地图展示、交互、分析等功能。本文将介绍当前市面上常用的GIS前端开发框架,并进行对比分析。 Leaflet Leaflet是一款轻量级的开源地图库,它提供了丰…...

[论文精读]TorWard: Discovery, Blocking, and Traceback of Malicious Traffic Over Tor
期刊名称:IEEE Transactions on Information Forensics and Security 发布链接:TorWard: Discovery, Blocking, and Traceback of Malicious Traffic Over Tor | IEEE Journals & Magazine | IEEE Xplore 中文译名:TorWard:…...

pytest - 多线程提速
import timedef test1_test1():time.sleep(1)assert 1 1, "11"def test1_test2():time.sleep(1)assert 1 1, "11" 上面2个函数,执行情况: 正常执行时,花费 2.08s2个进程执行时,花费 1.18s2个线程执行时&a…...

python中logging的用法
logging.error 是 Python logging 模块中的一个方法,专门用于记录错误级别(ERROR)的日志信息。logging 模块是 Python 提供的标准日志工具,用于生成各种级别的日志消息,并支持日志的格式化和存储。 logging.error 的基…...

【YOLO目标检测车牌数据集】共10000张、已标注txt格式、有训练好的yolov5的模型
目录 说明图片示例 说明 数据集格式:YOLO格式 图片数量:10000(2000张绿牌、8000张蓝牌) 标注数量(txt文件个数):10000 标注类别数:1 标注类别名称:licence 数据集下载:车牌数据…...

gdb xterm 调试 openmpi 程序
1,编写编译一个openmpi程序 迭代计算 PI 的源程序: pi_reduce.c #include <stdio.h>#include <math.h> #include <mpi.h>double f(double); double f(double x) {return (4.0/(1.0x*x)); }int main(int argc, char* argv[]) {int d…...