0

I've encountered this strange situation(JAVA):

I have a thread like this:

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

and my update() :

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

If I comment the logging method (System.out.println()), it never sees if getPosition().x()<0.

But if I de-comment the line I logged:

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

it sees now if the x is lesser than 0.

getPosition() :

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

Vector2.x() :

public double x() {
    return x;
}

I could not find what is wrong really. Any help will be appreciated, thanks!

Edit:

It occurs because of cpu consumption I'm nearly sure. Because I tried to add a time of sleep to the call scope of "update()" it worked but I don't want this thread to sleep. Any ideas?

Pang
  • 9,564
  • 146
  • 81
  • 122
ZeRoHuK
  • 13
  • 5

1 Answers1

0

@kordirko It works, thank you! Btw I hadn't use a volatile before

You are welcome, I am glad I could help.

However, since you are using threads, you need to know some facts about volatile and not volatile variables and review your code to avoid another subtle synchronization pitfalls.


From the Java specification:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

17.7. Non-Atomic Treatment of double and long

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Writes and reads of volatile long and double values are always atomic.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.

The above means, that if you share some long and double non-volatile variables between two or more threads, then even if the code seems to work correctly, from time to time the code will fail, because one thread will see only a partial updated value of the variable.
You have to declare them as volatile.


And from one answer on Stack Overflow:
Volatile variable in Java

When is volatile needed ?

When multiple threads using the same variable, each thread will have its own copy of the local cache for that variable. So, when it's updating the value, it is actually updated in the local cache not in the main variable memory. The other thread which is using the same variable doesn't know anything about the values changed by the another thread. To avoid this problem, if you declare a variable as volatile, then it will not be stored in the local cache. Whenever thread are updating the values, it is updated to the main memory. So, other threads can access the updated value.

The above explains why your code worked fine when //System.out.println(getPosition().x()); was uncommented, and failed when this line was commented.
The volatile keyword simply means to the compiler "I am going to share this field between many threads, please synchronize access to this field and always retrieve it's actual value directly from the memory".
The field x was not declared as volatile, so the compiler didn't synchronize it, and optimized this fragment of code in such a way, that a value of the variable wasn't retrieved directly from the memory, but from a local cache (most likely stored in processor's registers). The thread didn't know that another thread updated a value of this field in the memory, it saw only a value from the local cache.
When a call to println was uncommented, then the value of x was flusched from the cache by this call, and retrieved directly from the memory after the call had finished - in this way the thread saw the updated value.

Community
  • 1
  • 1
krokodilko
  • 35,300
  • 7
  • 55
  • 79
  • Do not use volatiles so readily in performance critical context. They have VERY strong happens-before guarantees for referencing threads and are relatively slow. Use Atomic* classes when context to synchronize only consists of a single value. – Basilevs May 15 '16 at 06:29