简体   繁体   中英

Java volatile variables and synchonized getters

I am doing some experiments with java concurrency with book Java "concurrency in practise".

I have next question: Is the syncronized getter method equals with volatile private variable? Some code:

public class ConsoleApp {

private static boolean ready;
private static int number;



public synchronized static boolean isReady() {
    return ready;
}



private static class ReaderThread extends Thread {
    public void run() {
        while (!isReady()) {
            //Thread.yield();  // jvm updates variables on sleeping thread sometimes 
        }
        System.out.println("from class: " + number);
    }
}



public static void main ( String [] arguments ) throws InterruptedException
{
    System.out.println("start");
    Thread.sleep(3000);

    ready = false;
    number = 23;

    System.out.println("inited variable");
    Thread.sleep(3000);

    new ReaderThread().start();

    System.out.println("thread started");
    Thread.sleep(3000);

    number = 42;
    ready = true;

    System.out.println("variables changed");
    Thread.sleep(3000);


    System.out.println("ended;: " + number);
    Thread.sleep(8000);
    System.out.println("end" + number);

}
}

This variant give me "from class: " message. Like as

private static volatile boolean ready;
...
while (!ready) {
    ...
}

What is the difference in terms of code execution? I know that "synchonized" means "one thread access", and volatile means "get value from main thread without caching"

But why is the behavior of two variants is same??

Is the synchronized getter method equals with volatile private variable?

No.

The equivalence is with a synchronized setter AND a synchronized getter versus a volatile private variable.

If you use a synchronized getter, and a plain (non-synchronized) setter, there will be no happens-before relationship between one thread's write and another thread's (subsequent) read of the variable.

The other point is that the getter and setter need to synchronize on the same thing.

Finally, a getter or setter may do something more complicated than reading or writing a variable. In that case, the synchronized construct ensures that all of the actions are performed atomically with respect to other threads synchronizing on the same lock. By contrast, a volatile variable cannot give you an atomicity guarantee.


But why is the behavior of two variants is same?

You got lucky!

The Java Memory Model says when a read operation is guaranteed to see the result of a previous read operation. If you do it the right way your application will behave correctly.

The converse is not the case. If you do it wrong, you may get the correct behavior anyway(!) but it is not guaranteed:

  • You may always get it on a particular platform; eg hardware with a given number of cores and given memory architecture.

  • You may always get it with a given version of Java.

  • Or you may get the correct behavior ... except in rare or unusual circumstances.

But there are no behavioral guarantees if you don't do what the Java Memory Model requires.


A couple of consequences:

  • Testing won't prove that your correct concurrent code is correct. At best, it may show that it is incorrect.

  • Testing to prove that incorrect concurrent code is incorrect is also difficult. You may find that your incorrect code seems to work correctly.

Only releasing and subsequently acquiring lock establishes happens-before relation. So you need to use synchronized method for both getting and setting value fro a variable ready .

But in your example making ready volatile is enough as setting new value fro variable and getting value from variable also establish happens-before .

The main thing is that you need to be consistent. If you use synchronized methods for variable then all access to that variable should be done via those synchronized methods only.

Example in your question will always work predictably with volatile but with only setter marked as synchronized there is no happens-before between

ready = true;

in main thread and

while (!isReady()) 

in second thread. So JVM memory model guarantees nothing here. It might work as you expect it might work not the way you expect.

The thing with concurrency is that you need to lower your expectations of certainty and predictability. The computer was doing that when you were doing things single-thread. Now that you're dealing with concurrency it won't. You can't ask "why are this or that alike" or "why are this or that different"? All you can ask is whether something is ensured, or not ensured.

The two versions of code happen to give the same results when you try it on your computer using your versions of your tools, because nothing mandate that they will behave differently. Nothing mandates either that they will do the same thing. Something will happen with these codes, and you can predict some of it and not the rest.

Anyway, the difference with synchronized method or volatile variable, is that any access, read or write, to a volatile variable is guaranteed to be made as if there was only one main memory and thread local memory caches didn't exist. While synchronized methods only do that when you enter the synchronized method. Here you're only synchronizing on reading the variable, and you're not doing any proper handling on writing.

That means your example with synchronize, had a lot of chances to fail to give the expected results. Not that it was guaranteed to fail. Maybe it will, maybe it won't. All we can tell is what is guaranteed by proper programming. We can't tell what will happen with what your programming left unknown.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM