本文记录“System.out.println”输出保证可见性

正常使用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)**

### 3. **JVM 的优化行为**

### 4. **实际效果**

### 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 提供的同步工具来明确地保证可见性和线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值