Java:在没有易失性变量的情况下关闭?

Java:在没有易失性变量的情况下关闭?,java,concurrency,signals,volatile,shutdown,Java,Concurrency,Signals,Volatile,Shutdown,以下场景: 我有一个应用程序,运行了几个星期,然后我想优雅地关闭它 下面的代码实现了这一点: Main-Thread: boolean volatile active=true; while(active) { //loop-code (very fast) } //shutdown-thread, called once after a few weeks active=false; 现在,在每次循环迭代之后,我都会在主内存中查找,这是导致易失性读取的原因(对吧?!) 我不

以下场景:

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

下面的代码实现了这一点:

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
    }
}
boolean volatile active=true;
while(活动)
{

对于(int i=0;i而言,对于缓存一致性系统,您认为易失性读取总是命中主存的假设并不成立。在另一个线程修改标志并使变量所在的缓存线无效之前,易失性读取应该命中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);

如果您想知道所有这些访问模式是什么,这里有一个很好的总结:。

对于缓存一致性系统,您认为易失性读取总是命中主存的假设并不成立。易失性读取应该命中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);

如果您想知道所有这些访问模式是什么,这里有一个很好的总结:。

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