有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java>volatile和final:volatile作为flushingallmemorycontent

请看下面的答案(1):

https://stackoverflow.com/a/2964277/2182302Java Concurrency : Volatile vs final in "cascaded" variables?

关于我的老问题(2):

one java memoryFlushing volatile: A good programdesign?

据我所知(见(2)),我可以使用volatile变量作为所有内存内容的内存屏障/刷新器,而不仅仅是volatile关键字引用的内容

现在(1)中被接受的答案是,它只会刷新连接易失性keyowrd的内存

那么现在什么是正确的呢?,如果(2)中的全部刷新原理是正确的,为什么我不能将volatile与final结合起来附加到变量上


共 (1) 个答案

  1. # 1 楼答案

    两个答案都不正确,因为你的想法是错误的。“刷新内存”的概念很简单。它不在Java虚拟机规范中。这不是一件事。是的,许多CPU/体系结构确实是这样工作的,但JVM却不是这样

    你需要按照JVM规范进行编程。如果不能做到这一点,意味着你每次编写的代码都能在你的机器上正常工作,然后你把它上传到你的服务器上,结果它就失败了。这是一个可怕的场景:有错误的代码,但测试永远无法触发的错误。Yowza,那些很糟糕

    那么,JVM规范中的是什么

    不是“冲洗”的概念。它确实有HBHA的概念:发生在之前/之后。下面是它的工作原理:

    1. 有一个特定交互的列表,其中设置了一些代码行被定义为“之前发生”(HB/HA=之前发生/之后发生)另一行。下面给出了这个列表的想法
    2. 对于具有HBHA关系的任何两条线路,HA线路都不可能观察到任何状态,以至于HB线路似乎尚未运行。它基本上是说:HB线出现在HA线之前,但不是很强:你不能观察到相反的情况(即HB改变变量X,HA线没有看到X的变化,这是观察到相反的情况,这是不可能的)。除了时间方面。事实上,HB/HA实际上并不意味着行可以更早或更晚地执行:如果有两行具有HB/HA关系,并且彼此没有影响(一行写入变量X,另一行读取完全不同的变量Y),那么JVM/CPU可以自由地重新排序
    3. 对于没有定义HB/HA关系的任何两行,JVM和CPU可以自由地做任何它喜欢做的事情。包括不能用简单的“冲洗”模型解释的事情

    例如:

    int a = 0, b = 0;
    
    void thread1() {
        a = 10;
        b = 20;
    }
    
    void thread2() {
        System.out.println(b);
        System.out.println(a);
    }
    

    在上面的例子中,在修改a/b状态的线程1和读取它们的线程2之间没有建立HB/HA关系

    因此,JVM打印20 0是合法的,尽管这不能用基本的刷新概念来解释:JVM“刷新”b是合法的,而不是a

    你不太可能写出这段代码,并在任何JVM版本或任何硬件上实际观察到20/0打印,但重点是:它是允许的,有一天(或者可能已经存在),JVM+硬件+操作系统版本+机器状态的某种奇特组合会真正实现这一点,因此,如果您的代码在这一系列事件发生时中断,那么您编写了一个bug

    实际上,如果一行变异了状态,而另一行读取了状态,而这两行没有HB/HA,你就搞砸了,你需要修复错误。甚至(尤其是!)如果你不能写一个测试来证明这一点

    这里的诀窍是volatile readsdo建立HB/HA,而这是JVM规范必须同步内容的唯一机制,是的,这可以保证您“看到所有更改”。但这根本不是一个好主意。特别是因为JVM还表示hotspot编译器可以自由删除没有副作用的行

    因此,现在我们必须就“建立HBHA”是否是副作用展开辩论。可能是这样,但现在我们来讨论优化规则:

    编写惯用代码

    每当azul、openjdk核心开发团队等考虑改进热点编译器的大量优化功能时,他们都会查看实际代码。这就像一个巨大的模式匹配器:他们在代码中寻找模式并找到优化它们的方法。他们不只是为所有可以想象的东西编写检测器:他们强烈地预测为实际java代码中常见的模式编写优化器。毕竟,花时间和精力优化一个几乎没有java代码实际包含的构造,有什么可能呢

    这就引出了使用一次性易变读作为建立HB/HA的一种方法的基本问题:没有人这样做,因此,在某个时刻JVM被更新的几率(或者简单地说,冲突规则被“解释”为含义:是的,hotspot可以消除无意义的读取,即使它确实建立了一个现在不再存在的HB/HA)相当高——如果你以独特的方式做事,你也更有可能遇到JVM错误。毕竟,如果你做事情的方式很好,那么这个错误早就被报告并修复了

    如何建立HB/HA:

    1. 自然规律是:在一个线程中,除了顺序之外,不能观察到代码以任何方式运行,即在一个线程中,所有行都以明显的方式彼此具有HB/HA

    2. 同步块:如果一个线程退出同步块,然后另一个线程在同一引用上进入一个,那么a中的同步块退出发生在同步块进入B之前

    3. 易失性读写

    4. 一些奇特的东西,比如:thread.start()发生在线程的run()方法的第一行之前,或者线程中的所有代码都保证在该线程的thread.yield()完成之前被释放。这些往往是显而易见的

    因此,要回答这个问题,它是好的编程设计吗

    不,不是

    以正确的方式建立HB/HA:在^{中找到合适的东西并使用它。从简单锁到队列,再到整个作业的fork/join池。或者,停止共享状态。或者,与设计用于以比HB/HA更自然的方式进行并发访问的机制共享状态,例如数据库(事务)或消息队列