【问题标题】:Java: Shutdown without a volatile variable?Java:在没有 volatile 变量的情况下关机?
【发布时间】:2019-12-17 18:40:28
【问题描述】:

以下场景:

我有一个应用程序,它运行了几个星期,然后我想优雅地关闭它。

下面的代码可以解决问题:

Main-Thread:

boolean volatile active=true;

while(active)
{
    //loop-code (very fast)
}


//shutdown-thread, called once after a few weeks

active=false;

所以现在在每次循环迭代之后,我都会在主内存中查找,这是导致易失性读取的原因(对吗?!)。

我不想那样做,只是为了几周后关闭。

还有其他解决方案,让我的主线程收到有关关闭的通知吗?

任何直接进入主线程缓存的信号?这样它就不用每次都在主存中查找自己,而是从外部获得通知?

或者任何其他解决方案?

编辑(将我自己的答案整合到这个问题中):

一种可能的解决方案是,减少易失性访问,请参见以下代码:

boolean volatile active=true;


while(active)
{
    for(int i=0; i<100; ++i)
    {
        //loop-code
    }
}

因此,使用该解决方案我可以减少易失性读取,但我会在关闭后将最大循环迭代次数从 1 增加到 100。

该解决方案减少了 volatile 访问,但不能完全消除它。

【问题讨论】:

  • 您是否有理由在没有停顿的循环中运行“非常快”的代码?这将使用大量的 CPU。该循环代码需要多久执行一次?
  • @Eric 对于一个简单的退出标志,不透明的访问就足够了,而且确实影响最小。但它至少需要 Java 9。
  • @RobinKreuzer “之前”不是一个有用的术语,因为您在每次迭代中重复读取 volatile 变量。通常,JVM 可以在假设它是唯一一个修改堆变量(即忽略其他线程更改的可能性)的情况下优化代码,这允许将值保留在 CPU 寄存器中,但也可以预测后续条件的结果基于先前值的迭代等等。易失性读取的存在使其考虑执行相应写入的线程所做的潜在更改。
  • 作为一个极端的例子,for(int i = 0; i &lt; 1_000_000; i++) heapVar++; 可能会优化为heapVar += 1_000_000;,而for(int i = 0; i &lt; 1_000_000 &amp;&amp; volatileBooleanFlag; i++) heapVar++; 将继续进行一百万次增量。这不是必需的,这个特定示例的语义仍然允许执行if(volatileBooleanFlag) heapVar += 1_000_000;,但众所周知,HotSpot JVM 在优化代码时非常保守,包括 volatile 读取并且不会这样做。
  • 基本上你的描述是对的,不透明模式保证了变化的可见性,但对其他变量没有影响。如前所述,理论上,此处描述的操作适用于所有模式,但在实践中不会发生。 opaque 模式是仍然保证可见性的最弱模式。当某个特定的 JVM 仍未对其进行优化时,无论如何也没有更好的解决方案。

标签: java concurrency signals volatile shutdown


【解决方案1】:

您认为易失性读取总是命中主内存的假设不适用于缓存一致的系统。 volatile 读取应该命中 L1,直到另一个线程修改标志并使变量所在的缓存行无效。

但是,易失性读取与后续访问建立了先发生关系,因此这可以防止编译器和 CPU 执行某些隐藏延迟的技巧。相反,使用不透明的访问模式来减少影响(感谢 Holger :))。

这样的事情应该很快,但我会将基准测试留给你:

AtomicBoolean active = new AtomicBoolean(true);

while(active.getOpaque())
{
    //loop-code (very fast)
}


//shutdown-thread, called once after a few weeks

active.setOpaque(false);

如果您想知道所有这些访问模式是什么,这里有一个很好的总结:Using JDK 9 Memory Order Modes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多