在多线程下的long和double

探讨了Java内存模型下volatile关键字对long和double类型变量的重要性,解释了如何使用volatile解决多线程环境下数据一致性问题。

在看一些代码的时候,会发现在定义long型和double型的变量时,会在前面加上volatile关键字,当然也会看到在其它原子类型的变量前加上这个关键字,但这里要说的还是有区别的。

 

在java中,java的内存模型要求,变量的读取操作和写入操作都必须是原子操作的,但是对于非volatile类型的long和double有些不同,因为这两个变量是64位存储,JVM允许将64位的读操作或写操作分解为2个32位的操作。这样,当在多线程环境中读取一个非volatile的long变量时,可能会出现读取到这个变量一个值的高32位和另一个值的低32位,从而导致数据出问题。

 

这里还需要说明下volatile关键字的作用,可以说有2个作用,其一是,用volatile修饰的变量的读取和写入都是直接操作内存,以保证被其它线程读取到值都是最新的,或者称之为确保内存的可见性;其二是,保证变量的读取和写入操作都是原子操作,就是上面long和double的读取所遇到的问题,注意这里提到的原子性只是针对变量的读取和写入,并不包括对变量的复杂操作,比如i++就无法使用volatile来确保这个操作是原子操作。

 

所以,在多线程环境中,要确保long和double变量的数据正确,可以使用volatile关键字修饰变量,也可以采用同步机制来保证数据的正确性。

 

另外《java并发编程实战》中也讲到了使用volatile的需要满足的所有条件:

a、对变量的写入操作不依赖于变量当前的值,或者你能确保只有单个线程更新变量的值

b、该变量不会与其他状态变量一起纳入不变性条件中

c、在访问变量时不需要加锁

在 **Java** 中,`long` `double` 的 **写操作** 在 **32 位 JVM** 上 **不是原子性** 的,但在 **64 位 JVM** 上,如果数据是 **自然对齐(naturally aligned)** 的,则可能是原子性的。 --- ## **1. 为什么 `long` `double` 可能不是原子性的?** - **32 位 JVM**: - `long`(64位) `double`(64位)在 32 位机器上需要 **两次 32 位操作** 来完成写,可能导致 **数据撕裂(tearing)**(即只写入/取一半数据)。 - 例如: ```java // 线程 A 写入 long value = 0x1234567890ABCDEFL; // 分两次写入(高32位 + 低32位) ``` ```java // 线程 B 取时,可能到中间状态(如仅写入 0x12345678,而 0x90ABCDEF 还未写入) ``` - **64 位 JVM**(现代 Java 版本): - 如果 `long` `double` 是 **自然对齐(8字节对齐)**,则它们的写通常是 **原子性** 的。 - 但 **仍建议使用 `volatile` 或 `AtomicLong` 来确保原子性**,因为 JVM 实现可能不同。 --- ## **2. 如何确保 `long` `double` 的原子性?** ### **(1) 使用 `volatile` 关键字** ```java private volatile long value; // 保证写原子性 + 可见性 private volatile double value; ``` - **作用**: - 防止 JVM 进行 **指令重排序优化**。 - 确保 **多线程下的可见性**(写入后立即刷新到主内存)。 ### **(2) 使用 `AtomicLong` `AtomicDouble`** ```java private AtomicLong atomicLong = new AtomicLong(0L); private AtomicReference<Double> atomicDouble = new AtomicReference<>(0.0); ``` - **优点**: - 提供 `get()`, `set()`, `compareAndSet()` 等 **线程安全方法**。 - 适用于 **高并发场景**(如计数器)。 ### **(3) 使用 `synchronized` 锁** ```java private long value; public synchronized void setValue(long v) { this.value = v; } public synchronized long getValue() { return this.value; } ``` - **适用场景**: - 当需要 **复合操作**(如 `value++`)时,`synchronized` 比 `volatile` 更安全。 --- ## **3. Java 语言规范(JLS)的规定** 根据 **Java 语言规范(JLS 17.7)**: - **`long` `double` 的写** 在 **32 位 JVM** 上 **不是原子性** 的(除非使用 `volatile`)。 - **`int`、`float`、`boolean`、`byte`、`char`、`short`** 的写总是 **原子性** 的(即使在 32 位 JVM)。 > **📌 最佳实践**: > **即使 `long` `double` 在 64 位 JVM 上是原子性的,仍建议使用 `volatile` 或 `AtomicLong` 来避免潜在问题。** ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值