java多线程可见性问题

arnold
  • 110

《java并发编程实战》一书中第三章 可见性章节中说

public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}

“该代码可能会输出0,也可能持续循环下去,因为读线程可能永远都看不到ready的值

输出0,还能理解,因为number没有加volatile。
为什么可能“永远都看不到ready的值”?

回复
阅读 2.4k
4 个回答
北上求道
  • 117

我看这个volatile相关文章不下于五篇 感觉都不是说的太好 相似度很高 我先说一下自己的理解 每一个线程都对应一个主存空间 例如你例子中的number 第一次时 从主存中读取number的值 然后把它放进一个缓存cache中 从单线程中看 是没问题的 但是在多线程中 如果对这个number做了读写操作 那么就会出现数据的不一致性 例如 线程1 从主存中读取number=10 放进缓存中 线程2读取缓存中的number值等于10然后进行加减操作 再还没有写进主存时 线程3又读取了缓存中的值 还是10!!! 加volatile关键字保证数据的可见性我是这样理解的 就是每次这个number发生变化时 都会对其他读取number值的线程发出警告 告诉它们 这个值发生变化了 你要去主存中获取最新的值 实际上还可能出现数据不一致问题 因为警告只是警告 并没有限制线程进行的操作而已 还有一个是禁止指令重排序在多线程中对结果产生的影响(这个东西可以去看看相关文章 jvm里面的方法了) volatile相较于Synchronized是一种轻量级的保证数据同步的修饰符 以上供你参考 顺带提一句 我看spring源码里面 目前看见的volatile关键字修饰的都是类级别的属性 例如 volatile class<?> xxx这类的 所以感觉各种博客把volatile放一个时常变化的基础数据类型变量上做例子 是不是欠考量了

大致可以理解主线程只要一直不刷新ready的值,ReaderThread就一直认为ready的值为false,主要是理解volatile变量的意义,还有一个因素就是线程调度,yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

可以读另外一本叫《java并发编程的艺术》了解为什么,大概意思就是没有加volatile的话,和可能这个cache一直是线程cache,前年校招的时候读过一次。。

number一样啊一直读的是本线程内存,缓存的false

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏