如果我将新值记录到控制台,条件表达式将检查这些值



我遇到过这种奇怪的情况(JAVA):

我有一条这样的线索:

public void run()
        {
            awake();
            start();
            while(this!=null)
                update();
            return;
        }

和我的更新()

public void update()
{
    //System.out.println(getPosition().x());
    if(getPosition().x()<0)
    {
        getPosition().setX(800);
        moveInTime(velocity,9,10);
    }
}

如果我对日志记录方法(System.out.println())进行注释,它永远看不到getPosition().x()<0.

但如果我取消评论我记录的行:

public void update()
{
    System.out.println(getPosition().x());
    if(getPosition().x()<0)
    {
        getPosition().setX(800);
        moveInTime(velocity,9,10);
    }
}

它现在看到x是否小于0。

getPosition()

public Vector2 getPosition() {
    return this.position;
}

Vector2.x()

public double x() {
    return x;
}

我找不出真正的问题。任何帮助都将不胜感激,谢谢!

编辑:

我几乎可以肯定,这是因为cpu消耗。因为我试图在"update()"的调用范围中添加睡眠时间,所以它成功了,但我不希望这个线程睡眠。有什么想法吗?

@科尔迪尔科,真管用,谢谢!顺便说一句,我在之前没有使用过挥发性物质

不客气,我很高兴能帮上忙

但是,由于您使用的是线程,因此需要了解有关易失性和非易失性变量的一些事实,并检查代码以避免另一个微妙的同步陷阱。


根据Java规范:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

17.7.双和长的非原子处理

为了Java编程语言内存模型的目的对非易失性长值或双值的单次写入被视为两次写入单独写入:每个32位的一半写入一个。这可能导致线程从中看到64位值的前32位的情况一次写入和来自另一次写入的第二个32位。

易失性长值和双值的写入和读取始终是原子的。

对引用的写入和读取始终是原子的,无论无论它们是实现为32位值还是64位值。

上面的意思是,如果你在两个或多个线程之间共享一些长的双非易失性变量,那么即使代码看起来工作正常,代码也会不时失败,因为一个线程只会看到变量的部分更新值
你必须声明它们是不稳定的。


从堆栈溢出的一个答案来看:
Java 中的易失性变量

什么时候需要挥发性物质

当多个线程使用同一个变量时,每个线程将具有该变量的本地缓存的自身副本。所以,当更新值时,它实际上是在本地缓存中更新的,而不是在主变量存储器。使用相同的另一个线程变量对另一个变量更改的值一无所知线为了避免这个问题,如果您将变量声明为volatile,则它将不会被存储在本地高速缓存中。只要线程更新值时,它被更新到主存储器。所以,其他线程可以访问更新后的值。

上面解释了为什么当//System.out.println(getPosition().x());被取消注释时代码工作良好,而当这一行被注释时代码失败
volatile关键字对编译器来说只是意味着"我将在多个线程之间共享这个字段,请同步对这个字段的访问,并始终直接从内存中检索它的实际值"
字段x没有被声明为volatile,因此编译器没有同步它,并以这样的方式优化了这段代码,即变量的值不是直接从内存中检索的,而是从本地缓存(很可能存储在处理器的寄存器中)中检索的。线程不知道另一个线程更新了内存中该字段的值,它只看到了本地缓存中的一个值
当对println的调用被取消注释时,x的值就会被这个调用从缓存中清除,并在调用完成后直接从内存中检索——这样线程就会看到更新后的值。

最新更新