Java-并发编程-特性-可见性-synchronized如何保证可见性?
synchronized 能保证可见性吗?
在Java并发编程中,synchronized 关键字不仅用于实现互斥访问,还能够保证内存可见性。理解这一点需要了解Java内存模型(Java Memory Model,JMM)以及happens-before(先行发生)原则。
1. Java内存模型(JMM)概述
Java内存模型定义了Java虚拟机(JVM)如何与计算机的内存(主内存)交互,以及如何在多线程环境下保证线程之间的可见性和有序性。主要涉及两个重要概念:
- 主内存(Main Memory):所有线程共享的内存区域,用于存储类的字段、实例变量等。
- 工作内存(Working Memory):每个线程独立拥有的内存空间,用于存储线程使用的变量的副本。
线程对变量的操作主要在工作内存中进行,必须通过读写主内存实现线程间共享数据。
2. synchronized 的双重作用
sychronized 关键字在Java中扮演着关键角色,主要有以下两个作用:
-
互斥访问(Mutual Exclusion):
- 保证同一时间内只有一个线程可以执行被
synchronized修饰的代码块或方法,防止竞态条件(Race Condition)的发生。
- 保证同一时间内只有一个线程可以执行被
-
内存可见性(Memory Visibility):
- 确保进入
synchronized块之前,线程看到的主内存中共享变量的最新值。 - 线程退出
synchronized块时,会将工作内存中的修改立即刷新回主内存。
- 确保进入
2.1 互斥访问
通过锁机制,synchronized 确保了同一时间只有一个线程可以持有锁并执行同步代码。这防止了多个线程同时修改共享资源可能导致的数据不一致问题。
2.2 内存可见性
synchronized 通过内存屏障(Memory Barrier)机制实现内存可见性,具体表现在以下两个方面:
- 进入同步块前:
- 线程会清空自己的工作内存,从主内存中重新读取同步变量的最新值。
- 退出同步块后:
- 线程会将工作内存中的修改刷新到主内存,确保其他持有同一锁的线程能够看到最新的值。
3. happens-before 原则
Java内存模型中,happens-before 关系定义了操作之间的执行顺序,并确保happens-before 关系中的先行操作对后续操作可见。
synchronized 的内存可见性保障基于以下 happens-before 规则:
-
解锁(Unlock)操作 happens-before 加锁(Lock)操作:
- 一个线程在释放锁之前对共享变量所做的所有写操作,happens-before 另一个线程随后获得同一锁时对这些共享变量的读取操作。
举例说明:
class Counter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;} }- 线程A调用
increment()方法并释放锁,线程B随后调用getCount()方法获取锁时,B线程能够看到A线程对count所做的修改。
-
当前线程的操作 happens-before 同步块内的操作:
- 这确保了在同一个线程内,进入
synchronized块之前的所有操作对synchronized块内可见。
- 这确保了在同一个线程内,进入
4. 实例解析
4.1 内存可见性示例
考虑以下示例,展示sychronized如何保证内存可见性:
class SharedData {private int value = 0;public synchronized void setValue(int newValue) {value = newValue;}public synchronized int getValue() {return value;}
}public class VisibilityDemo {public static void main(String[] args) {SharedData data = new SharedData();// 线程A:设置值Thread threadA = new Thread(() -> {data.setValue(42);System.out.println("ThreadA set value to 42");});// 线程B:获取值Thread threadB = new Thread(() -> {int val = data.getValue();System.out.println("ThreadB read value: " + val);});threadA.start();threadB.start();}
}
解释:
threadA调用setValue(42)并释放锁,value的值被更新并刷新到主内存。threadB调用getValue()并获取锁,读取到的value是最新的42。
如果没有synchronized,threadB 可能读取到旧的值0,因为线程间没有保证可见性。
4.2 不使用 synchronized 的可见性问题
class SharedData {private int value = 0;public void setValue(int newValue) {value = newValue;}public int getValue() {return value;}
}public class VisibilityIssueDemo {public static void main(String[] args) {SharedData data = new SharedData();// 线程A:设置值Thread threadA = new Thread(() -> {data.setValue(42);System.out.println("ThreadA set value to 42");});// 线程B:获取值Thread threadB = new Thread(() -> {int val = data.getValue();System.out.println("ThreadB read value: " + val);});threadA.start();threadB.start();}
}
可能输出:
ThreadA set value to 42
ThreadB read value: 0
原因:没有synchronized保护,threadB 可能在 threadA 更新 value 之前读取到旧值,导致数据不一致。
5. synchronized 与其他同步机制的对比
5.1 volatile 与 synchronized
-
volatile:- 保证变量的可见性,但不保证原子性。
- 适用于单一变量的更新,如状态标志。
-
synchronized:- 保证可见性和原子性。
- 适用于需要进行复合操作(如检查-然后-执行)的场景。
示例:
class VolatileExample {private volatile boolean flag = false;public void setFlag() {flag = true;}public boolean getFlag() {return flag;}
}
适用于简单的标志位控制,但若需要对flag进行复合操作(如if (!flag) { flag = true; }),则需要synchronized确保原子性。
5.2 ReentrantLock 与 synchronized
-
synchronized:- 由JVM层面实现,语法简单。
- 隐式释放锁,无法手动中断。
-
ReentrantLock:- 由Java库提供,功能更丰富,如可中断锁请求、定时获取锁、实现公平锁等。
- 需要显式释放锁,使用更加灵活。
示例:
import java.util.concurrent.locks.ReentrantLock;class LockExample {private ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {lock.lock();try {return count;} finally {lock.unlock();}}
}
6. 注意事项
-
避免过大范围的同步:
- 过大的同步块会导致性能下降,尽量缩小
synchronized的范围,只保护必要的代码部分。
- 过大的同步块会导致性能下降,尽量缩小
-
防止死锁:
- 若多个线程需要获取多个锁,确保获取锁的顺序一致,避免循环等待导致死锁。
-
公平性:
- 默认的
synchronized锁是非公平的,即线程获取锁的顺序不保证。但是,可以使用ReentrantLock的公平策略来控制。
- 默认的
-
适当使用锁:
- 并非所有情况都需要使用
synchronized,在简单的只读数据或只写数据场景下,可以考虑其他并发机制,如volatile、Atomic类等。
- 并非所有情况都需要使用
7. 总结
sychronized 关键字在Java中不仅用于实现线程间的互斥访问,还通过内存屏障机制保证了内存的可见性。具体来说:
- 可见性保证:
synchronized通过强制刷新工作内存与主内存之间的数据,确保一个线程在退出synchronized块时对共享变量的修改对其他线程是可见的。 - 互斥性保证:
synchronized确保同一时间只有一个线程执行被同步的代码块,防止数据竞态。
因此,在需要保证线程安全的场景下,synchronized 是一种简单而有效的同步机制,既能确保数据的一致性,又能维护内存的可见性。
如果需要更高级的锁特性或更细粒度的控制,可以考虑使用java.util.concurrent包中的其他并发工具,如ReentrantLock、ReadWriteLock等。
相关文章:
Java-并发编程-特性-可见性-synchronized如何保证可见性?
synchronized 能保证可见性吗? 在Java并发编程中,synchronized 关键字不仅用于实现互斥访问,还能够保证内存可见性。理解这一点需要了解Java内存模型(Java Memory Model,JMM)以及happens-before࿰…...
iOS 权限管理:同时请求相机和麦克风权限的最佳实践
引言 在开发视频类应用时,我们常常会遇到需要同时请求相机和麦克风权限的场景。比如,在用户发布视频动态时,相机用于捕捉画面,麦克风用于录制声音;又或者在直播功能中,只有获得这两项权限,用户…...
【深入理解FFMPEG】命令行阅读笔记
这里写自定义目录标题 第三章 FFmpeg工具使用基础3.1 ffmpeg常用命令3.1.13.1.3 转码流程 3.2 ffprobe 常用命令3.2.1 ffprobe常用参数3.2.2 ffprobe 使用示例 3.3 ffplay常用命令3.3.1 ffplay常用参数3.3.2 ffplay高级参数3.3.4 ffplay快捷键 第4章 封装与解封装4.1 视频文件转…...
数据结构:二叉树—面试题(二)
1、二叉树的最近公共祖先 习题链接https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/ 描述: 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点…...
【C++高并发服务器WebServer】-6:信号
本文目录 信号的概念1.1 core文件1.2 kill命令1.3 alarm函数1.4 setitimer调用1.5 signal捕捉信号1.6 信号集1.7 内核实现信号捕捉的过程1.8 sigaction1.9 sigchld 信号的概念 信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,…...
《探秘人工智能:从基础到未来变革》
在当今科技飞速发展的时代,人工智能(AI)无疑是最具影响力和变革性的技术之一。从手机里智能语音助手到自动驾驶汽车,从智能医疗诊断到智能金融服务,人工智能已经渗透到我们生活和工作的方方面面,悄然改变着…...
【数据分享】1929-2024年全球站点的逐月平均能见度(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标!说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2024年全球气象站点…...
【PyTorch】3.张量类型转换
个人主页:Icomi 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术,能够处理复杂的数据模式。通过 PyTorch࿰…...
不解释快上车
聊一聊 最近有小伙伴问我有小红书图片和短视频下载的软件吗,我心想,下载那上面的图片和视频做什么?也许是自己没有这方面的需求,不了解。 不过话又说回来,有些很多下载器可能作者没有持续的维护,所以可能…...
C++红黑树详解
文章目录 红黑树概念规则为什么最长路径不超过最短路径的二倍?红黑树的时间复杂度红黑树的结构插入叔叔节点情况的讨论只变色(叔叔存在且为红)抽象的情况变色单旋(叔叔不存在或叔叔存在且为黑)变色双旋(叔叔不存在或叔叔存在且为黑…...
csapp2.4节——浮点数
目录 二进制小数 十进制小数转二进制小数 IEEE浮点表示 规格化表示 非规格化表示 特殊值 舍入 浮点运算 二进制小数 类比十进制中的小数,可定义出二进制小数 例如1010.0101 小数点后的权重从-1开始递减。 十进制小数转二进制小数 整数部分使用辗转相除…...
神经网络|(一)加权平均法,感知机和神经元
【1】引言 从这篇文章开始,将记述对神经网络知识的探索。相关文章都是学习过程中的感悟和理解,如有雷同或者南辕北辙的表述,请大家多多包涵。 【2】加权平均法 在数学课本和数理统计课本中,我们总会遇到求一组数据平均值的做法…...
Spring 框架:配置缓存管理器、注解参数与过期时间
在 Spring 框架中,可通过多种方式配置缓存具体行为,常见配置方法如下。 1. 缓存管理器(CacheManager)配置 基于内存的缓存管理器配置(以SimpleCacheManager为例) SimpleCacheManager 是 Spring 提供的简单…...
FPGA实现任意角度视频旋转(完结)视频任意角度旋转实现
本文主要介绍如何基于FPGA实现视频的任意角度旋转,关于视频180度实时旋转、90/270度视频无裁剪旋转,请见本专栏前面的文章,旋转效果示意图如下: 为了实时对比旋转效果,采用分屏显示进行处理,左边代表旋转…...
openlayer getLayerById 根据id获取layer图层
背景: 在项目中使用getLayerById获取图层,这个getLayerById()方法不是openlayer官方文档自带的,而是自己封装的一个方法,这个封装的方法的思路是:遍历所有的layer,根据唯一标识【可能是id,也可能…...
【Jave全栈】Java与JavaScript比较
文章目录 前言一、Java1、 历史与背景2、语言特点3、应用场景4、生态系统 二、JavaScript1、历史与背景2、语言特点3、应用场景4、 生态系统 三、相同点四、不同点1、语言类型2、用途3、语法和结构4、性能5、生态系统6、开发模式 前言 Java和JavaScript是两种不同的编程语言&a…...
设计模式-建造者模式、原型模式
目录 建造者模式 定义 类图 优缺点 角色 建造者模式和工厂模式比较 使用案例 原型模式 定义 类图 优缺点 应用场景 应用类型 浅克隆 深克隆 建造者模式 定义 将一个复杂的对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,…...
PTMD2.0-疾病相关的翻译后修饰数据库
翻译后修饰(PTMs,post-translational modifications)通过调节蛋白质功能参与了几乎所有的生物学过程,而 PTMs 的异常状态常常与人类疾病相关。在此,PTMD 2.0展示与疾病相关的 PTMs 综合数据库,其中包含 93 …...
【Git版本控制器--3】Git的远程操作
目录 理解分布式版本控制系统 创建远程仓库 仓库被创建后的配置信息 克隆远程仓库 https克隆仓库 ssh克隆仓库 向远程仓库推送 拉取远程仓库 忽略特殊文件 为什么要忽略特殊文件? 如何配置忽略特殊文件? 配置命令别名 标签管理 理…...
批量创建ES索引
7.x from elasticsearch import Elasticsearch# 配置 Elasticsearch 连接 # 替换为你的 Elasticsearch 地址、端口、用户名和密码 es Elasticsearch([http://10.10.x.x:43885],basic_auth(admin, XN272G9THEAPYD5N5QORX3PB1TSQELLB) )# # 测试连接 # try: # # 尝试获取集…...
模块初阶学习
当我们在过去想要实现一个功能时,例如Swap交换函数时,我们需要不断考虑参数的正确与否。如果是在c语言,我们还需要不断更改函数名字,以防止函数名重复。在c我们可以通过函数名重载解决这个问题,但还是有一些小问题&…...
rust学习-rust中的保留字
rust学习-rust中的保留字 已使用的保留字未来可能使用的保留字 保留字是语言中预定义的标识符,不能用作变量名、函数名或其他自定义标识符,Rust的保留字大致可以分为两类:已使用的保留字和未来可能使用的保留字 已使用的保留字 as࿱…...
MySQL中的读锁与写锁:概念与作用深度剖析
MySQL中的读锁与写锁:概念与作用深度剖析 在MySQL数据库的并发控制机制中,读锁和写锁起着至关重要的作用。它们是确保数据在多用户环境下能够正确、安全地被访问和修改的关键工具。 一、读锁(共享锁)概念 读锁,也称为…...
专利申请的价值
独占市场 一种产品只要授权专利权,等于在市场上拥有独占权。 政策奖励 各地方政府均出台响应文件, 对专利申请者进行奖励或者补助。 申报项目 申报高新技术企业、创新基金等 各类计划、项目的必要前提条件 专利申请 技术保护 防止新的技术与产品被他人 抄…...
使用 OpenCV 和 Python 轻松实现人脸检测
目录 一、准备工作 二、加载人脸检测模型 三、读取图像并进行人脸检测 四、处理视频中的人脸检测 五、优化人脸检测效果 六、总结 在人工智能和计算机视觉领域,人脸检测是一项非常基础且重要的技术。通过人脸检测,我们可以在图像或视频中识别并定位人脸,进而进行后续的…...
自然语言处理——从原理、经典模型到应用
1. 概述 自然语言处理(Natural Language Processing,NLP)是一门借助计算机技术研究人类语言的科学,是人工智能领域的一个分支,旨在让计算机理解、生成和处理人类语言。其核心任务是将非结构化的自然语言转换为机器可以…...
kotlin内联函数——runCatching
1.runCatching作用 代替try{}catch{}异常处理,用于捕获异常。 2.runCatching函数介绍 参数:上下文引用对象为参数返回值:lamda表达式结果 调用runCatching函数,如果调用成功则返回其封装的结果,并可回调onSuccess函…...
2025年新开局!谁在引领汽车AI风潮?
汽车AI革命已来。 在2025年伊始开幕的CES展上,AI汽车、AI座舱无疑成为了今年汽车行业的最大热点。其中不少车企在2025年CES上展示了其新一代AI座舱,为下一代智能汽车的人机交互、场景创新率先打样。 其中,东软集团也携带AI驱动、大数据支撑…...
YOLO目标检测3
一. 参考资料 《YOLO目标检测》 by 杨建华博士 本篇文章的主要内容来自于这本书,只是作为学习记录进行分享。 二. 搭建YOLOv1的网络 2.1 YOLOv1的网络结构 作者带我们构建的YOLOv1网络是一个全卷积结构,其中不包含任何全连接层,这一点可以…...
css3 svg制作404页面动画效果HTML源码
源码介绍 css3 svg制作404页面动画效果HTML源码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果 效果预览 源码如下 <!doctype html> <html> <head> <meta charse…...
