Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is "volatile" needed within methods?

I have read that you should use volatile when a thread accesses a variable to make sure that the threads see the correct value, but does that also apply when the variable is used within a method?

Example code:

public class Limiter
{
    int max = 0;

    public synchronized void doSomething()
    {
         max++;
         if(max < 10)
             System.out.println("Work");

         System.out.println(max);
    }

}

Is it safe to assume that if a multiple thread calls doSomething, then max will be set to the same state as when the previous Thread called the method?

Because doSomething() is synchronized, I know that only a single thread can modify max, but what happens when the next thread calls it? Can max be a different value because it doesn't use volatile? Or is it safe because the "Limiter" instance modifies it itself?

like image 550
Mstr Avatar asked Mar 19 '26 17:03

Mstr


2 Answers

volatile is part of the declaration of a field, not part of its use. It would make no sense to declare a local variable as volatile, as local variables won't be seen by different threads.

In your case, you're fine so long as no code in non-synchronized methods accesses max - the memory model basically makes sure that so long as all code acquires/releases the same monitor "guarding" a variable, all threads will see a consistent sequence of values. (Aside from anything else, the fact that the threads each have to acquire the monitor before accessing the value means only one thread will be able to access the value at a time - you could write a total ordering of "thread X had the monitor at time t0-t1, thread Y had the monitor at time t4-t5" etc.)

like image 119
Jon Skeet Avatar answered Mar 21 '26 06:03

Jon Skeet


I have read that you should use volatile when a thread accesses a variable to make sure that the threads see the correct value, but does that also apply when the variable is used within a method?

This is inaccurate in a number of respects*.

Firstly: volatile can only be used on fields. It cannot be used on other kinds of variables; i.e. local variables or parameters. (Reason: it would be pointless, because the other kinds of variable are only ever visible to one thread.)

Secondly: volatile semantics apply whether or not you are calling from a method.

Thirdly: volatile is an alternative to using synchronized methods (or blocks). But the correct use of synchronized typically conditional on all accesses and updates being performed using the synchronization construct. Any non-synchronized accesses or updates could render the program (as a whole) incorrect.

Finally: volatile means that accesses to the field will see the latest value of the field's value. This is not sufficient to guarantee correctness. For example:

public Counter {
    private volatile int count;

    public void increment() {
        // This is NOT correct.
        count++;
    }
}

The reason that the above example is incorrect is that count++ is not atomic. It actually means (literally) count = count + 1, and another thread could change count in between the current thread accessing it and writing back the updated value. This would result in occasional lost increments.

It is easy to see that a version of your code that declared max as volatile and got rid of the synchronized keyword would be incorrect for exactly the same reason. Your current version is more correct in that respect. However, it is still not entirely correct, because there is nothing stopping some other class (in the same package) from accessing or updating max. If that was done in a different thread, you'd have a potential concurrency issue ... in addition to the leaky abstraction.


So, to answer your questions:

Is it safe to assume that if a multiple thread calls doSomething, then max will be set to the same state as when the previous Thread called the method?

Not entirely, because of the leaky abstraction. (Declaring max to be private fixes this.)

Because doSomething() is synchronized, I know that only a single thread can modify max, but what happens when the next thread calls it?

The synchronized block ensures that the next thread that synchronizes on the same object will see those updates. (How this is achieved is platform specific ...)

Can max be a different value because it doesn't use volatile?

No ... modulo the leaky abstraction problem.

Or is it safe because the "Limiter" instance modifies it itself?

That fact is not relevant to that thread-safety / correctness of this code. It is synchronization that matters, not self-modification.


* At least, your summary of what you have understood by what you have read is inaccurate. For all we know, the original text that you read may be correct.


UPDATE

The part I read was talking about fields (as in variables accessible by the thread). Sorry for the confusion. What I wanted to know was if it also applied to all variables used by a method in an object called by a thread.

This applies to all fields of the object that the thread reads and/or writes are covered by the synchronized region. And indeed, it applies to the fields of other objects, and elements of arrays read / written in the region. (It is "moot" for variables that are not fields, because no other thread can see them.)

However, the caveat is that it ONLY applies with respect to the synchronization point. If the second thread does not synchronize properly, all bets are off.

like image 32
Stephen C Avatar answered Mar 21 '26 08:03

Stephen C