Java中System.out.println()为何会影响内存可见性

本文主要说明System.out.println为啥会影响内存的可见性?

先看示例代码:

public class T implements Runnable {
    private boolean flag = false;

    public boolean getFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        while (true){
            if(getFlag()){
                System.out.println(flag);
                System.out.println("Yeah!!!");
                break;
            }
        }
    }
}

这是一个实现了 Runnable 的自定义线程类,里面的 run 方法也很简单,它用 while 循环去检查 flag 变量是否为 true,如果为true,就打印字符,并且退出循环。

下面启动线程:

public class A {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();

        Thread thread = new Thread(t);
        thread.start();

        Thread.sleep(2000);

        t.setFlag(true);
    }
}

现在在另外一个类的 main 方法中去启动线程,然后等待2秒后将 flag 变量修改为 true。

这段代码一看就是有问题的,线程类T的run方法会一直循环下去,它感知不到 main 方法里面已经将 flag设置成了 true。
原因是这里存在两个线程,一个是线程T,一个是main所在的主线程。这两个线程的数据是不会相互干扰的。
也就是说这两个线程里面都存在一个flag的副本。真正的 flag 这个变量存放在主内存中,两个线程的工作内存中的 flag 变量只是副本。

这样理解没问题。因为我就是要验证这个现象。

但是接下来就会出现一个奇怪的现象!!!!

我先稍微的改动一下线程类T里面的run方法,我在里面加一句打印语句,改完之后如下所示:

@Override
 public void run() {
      while (true){
   		// 新增的打印
        System.out.println(111);
        if(getFlag()){
            System.out.println(flag);
            System.out.println("Yeah!!!");
            break;
        }
      }
 }

然后我现在再去运行main方法,却惊奇的发现if里面的语句打印出来了,线程T感知到了主线程对flag的改动。这是为什么?

在这里插入图片描述

线程T感知到了主线程对flag的改动,说明这条打印语句已经影响到了内存可见性。可是他是如何影响的呢?

答案就在源码里面:

 public void println(String x) {
	    synchronized (this) {
	        print(x);
	        newLine();
	    }
 }

原来println有一个上锁的操作。

使用了 synchronized 上锁会做以下操作:

  1. 获得同步锁;
  2. 清空工作内存;
  3. 从主内存拷贝对象副本到工作内存;
  4. 执行代码(计算或者输出等);
  5. 刷新主内存数据;
  6. 释放同步锁。

总结

这下明白了,为什么 System.out.println()方法会影响内存可见性了。
说得更细一点就是 System.out.println()方法中的 synchronized 影响了内存的可见性。


技 术 无 他, 唯 有 熟 尔。
知 其 然, 也 知 其 所 以 然。
踏 实 一 些, 不 要 着 急, 你 想 要 的 岁 月 都 会 给 你。


  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Java,你可以使用`LocalDevice`类来设置蓝牙设备的可见状态。以下是一个示例代码: ```java import javax.bluetooth.*; public class BluetoothVisibilityExample { public static void main(String[] args) { try { // 获取本地蓝牙适配器 LocalDevice localDevice = LocalDevice.getLocalDevice(); // 检查当前权限 if (!localDevice.isPowerOn()) { System.out.println("蓝牙适配器未开启,请先开启蓝牙适配器!"); return; } // 设置可见 boolean visibilitySet = localDevice.setDiscoverable(DiscoveryAgent.GIAC); if (visibilitySet) { System.out.println("蓝牙设备已设置为可见状态!"); } else { System.out.println("蓝牙设备可见设置失败!"); } } catch (BluetoothStateException e) { e.printStackTrace(); } } } ``` 在上述示例,我们首先通过`LocalDevice.getLocalDevice()`方法获取本地蓝牙适配器。然后,我们使用`isPowerOn()`方法检查蓝牙适配器是否已经开启。 接下来,我们使用`setDiscoverable()`方法设置可见。`setDiscoverable()`方法接受一个参数,用于设置可见模式。在示例,我们使用`DiscoveryAgent.GIAC`作为参数,表示通用可被发现模式。 最后,根据`setDiscoverable()`方法的返回值,我们可以判断是否成功设置蓝牙设备的可见。如果返回`true`,则表示可见已设置成功;如果返回`false`,则表示可见设置失败。 请注意,蓝牙设备的可见通常有时间限制,例如通常只能持续几分钟。如果你需要持久的可见,可能需要在代码定期调用`setDiscoverable()`方法来更新可见状态。 另外,为了确保代码的正常执行,你需要确保你的程序具有适当的蓝牙权限,可以参考我之前提供的关于申请蓝牙权限的示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值