正常使用volatile保证线程之间数据的可见性,但是今天才发现 输出 也可以保证数据的可见性,测试一下果然如此
原因是输出代码底层使用了synchronized 锁,这个锁保证了线程在进入代码块之前数据的同步(从主内存重新取数据),同时保证了代码块结束后数据的一致(将修改后的数据刷回主内存)。
测试代码:
package com.tuling;
public class Volatile_test1 {
private volatile boolean flag = true;
public void load(){
System.out.println(Thread.currentThread().getName() + "开始执行......");
System.out.println("while前的flag:" + flag);
while (flag){
//一直加载
//测试 输出 能不能保证可见性
System.out.println(flag);
}
System.out.println("while后的flag:" + flag);
}
public void refresh(){
/*结束加载*/
flag = false;
System.out.println(Thread.currentThread().getName() + "修改flag:" + flag);
}
public static void main(String[] args) throws InterruptedException {
Volatile_test1 test = new Volatile_test1();
Thread threadA = new Thread(()-> test.load(),"threadA");
threadA.start();
// Thread.sleep(1000);
Thread threadB = new Thread(()-> test.refresh(), "threadB");
threadB.start();
}
}
下面是今年最强AI——Deepseek(国产)给出的详解
目录
### 1. **`System.out.println` 的实现**
### 2. **内存屏障(Memory Barrier)**
### 5. **为什么不依赖 `System.out.println` 保证可见性?**
`System.out.println` 本身并不直接保证可见性,但在实际使用中,它常常表现出类似的效果。这是因为 `System.out.println` 内部的实现机制和 JVM 的优化行为间接地影响了线程的可见性。以下是详细原因:
---
### 1. **`System.out.println` 的实现**
`System.out.println` 是一个同步方法(`synchronized`),它的内部实现如下:
```java
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
```
- **同步机制**:`synchronized` 关键字会强制线程在进入和退出同步块时执行以下操作:
- 进入同步块时,线程会从主内存中重新加载所有变量的最新值(刷新工作内存)。
- 退出同步块时,线程会将工作内存中的修改写回主内存。
- 因此,`System.out.println` 的同步机制会间接地刷新线程的工作内存,使得其他线程对共享变量的修改变得可见。
---
### 2. **内存屏障(Memory Barrier)**
- `synchronized` 块会隐式地插入内存屏障,防止指令重排序,并确保变量的修改对其他线程可见。
- 当调用 `System.out.println` 时,JVM 会插入内存屏障,强制将线程的工作内存与主内存同步。
---
### 3. **JVM 的优化行为**
- JVM 会对代码进行各种优化,例如指令重排序。然而,`System.out.println` 是一个相对“重量级”的操作,涉及 I/O 操作和同步机制。
- 由于 I/O 操作本身比较耗时,JVM 可能会减少对 `System.out.println` 附近代码的优化,从而间接地减少了指令重排序的可能性,使得变量的修改更容易被其他线程观察到。
---
### 4. **实际效果**
- 在多线程调试中,`System.out.println` 经常被用来观察变量的值。由于上述原因,它通常会显示出变量的最新值,给人一种“保证了可见性”的错觉。
- 然而,这并不意味着 `System.out.println` 是一个可靠的同步工具。它的可见性效果是间接的,依赖于同步机制和 JVM 的行为,而不是设计上的保证。
---
### 5. **为什么不依赖 `System.out.println` 保证可见性?**
- **非确定性**:`System.out.println` 的可见性效果是偶然的,依赖于 JVM 的实现和优化行为,不能保证在所有情况下都有效。
- **性能问题**:`System.out.println` 是同步的,且涉及 I/O 操作,性能开销较大,不适合用于实际的同步需求。
- **设计原则**:Java 提供了明确的同步工具(如 `volatile`、`synchronized`、`java.util.concurrent` 包),应该使用这些工具来保证可见性和线程安全。
---
### 总结
`System.out.println` 之所以看起来“保证了可见性”,是因为它的同步机制和 I/O 操作间接地触发了内存同步。然而,这种行为是偶然的,不能依赖它来实现线程安全。在实际开发中,应该使用 Java 提供的同步工具来明确地保证可见性和线程安全。

1157

被折叠的 条评论
为什么被折叠?



