深入理解volatile与synchronized

本文介绍了Java中两种重要的并发控制机制:volatile和synchronized。volatile关键字保证了共享变量的可见性,降低了线程间同步的成本。synchronized则是一种更重的锁机制,但在Java SE 1.6中引入了优化。文章详细解释了这两种机制的工作原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.volatile

1.1.为什么要使用volatile

volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换。

1.2.volatile的定义

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。

1.3.volatile的实现原理

volatile保证可见性的原理是,有volatile变量修饰的共享变量进行写操作的时候,除了将操作数写回到缓存,还会发送一条Lock前缀的指令,这条指令做了两件事情:
1)将当前处理器缓存行的数据写回到系统内存;
2)这个写会内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

2.synchronized

2.1.简介

synchronized是一种重量级锁,但是,随着Java SE 1.6引入了偏向锁和轻量级锁之后,有些情况下它就并不那么重了。

2.2.synchronized的实现原理

JVM基于进入和退出monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用这两个指令来实现。
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且仅当一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

2.3.锁标记的存储

synchronized用的锁存在Java对象头中的Mark Word里。无锁状态下,Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。32位的JVM的Mark Word的默认存储结构如下表所示。
无锁状态下Mark Word的存储结构
锁状态25bit4bit1bit 是否是偏向锁2bit 锁标志位
无锁状态对象的HashCode对象的分代年龄001

在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。
偏向锁状态下Mark Word的存储结构
锁状态23bit2bit4bit1bit 是否是偏向锁2bit 锁标志位
偏向锁线程IDEpoch对象分代年龄101

轻量级锁状态下Mark Word的存储结构
锁状态30bit2bit 锁标志位
轻量级锁指向栈中锁记录的指针00

重量级锁状态下Mark Word的存储结构
锁状态30bit2bit 锁标志位
重量级锁指向互斥量(重量级锁)的指针10

GC标记状态下Mark Word的存储结构
锁状态30bit2bit 锁标志位
GC标记11

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值