3

I have been going through JCIP and there the author says..

A special case of thread confinement applies to volatile variables. It is safe to perform read-modify-write operations on shared volatile variables as long as you ensure that the volatile variable is only written from a single thread

For instance count++ is considered a compound operation (reading the value,adding one to it,and updating the value) and declaring count as volatile does not make this operation atomic, so thread safety is not guaranteed here !! Am i right ?? But here the author says we can fix it if we ensure that the volatile variable is only written from a single thread. I am failing to understand this. Please provide an illustration.

prvn
  • 684
  • 6
  • 21
  • Looks like the author meant that volatile is read safe in concurrent environment but not read-write safe (atomic); so if there are multiple reads but only one write at a given time it can be considered "thread safe"; the problem arises in cases on concurrent read-write. โ€“ Scorpion Sep 14 '15 at 10:54

4 Answers4

7

count++ is essentially two operations, read the value of count and store count + 1.

If two threads are trying to do count++ at the same time, they might both read the old value of count and increment it by one. So, at the end, if you don't add any synchronisation mechanisms you will end up with a final value of count + 1, instead of count + 2.

Volatile does not guarantee thread safety in such scenarios (not atomic), it only guarantees that the most recently stored value will be available to all threads. But this is not of much help in the scenario described above. You need to ensure, that no other thread will read the old value of count before the new one is stored.

If you need to have thread-safe counters, I'd suggest looking at AtomicInteger (and similar) classes. They provide an atomic version of count++; the incrementAndGet method.

alampada
  • 2,329
  • 1
  • 23
  • 18
6

A typical use case could look something like this:

public class Foo implements Runnable {
    private volatile int itemsProcessed;

    public int getItemsProcessed() { return itemsProcessed; }

    @Override
    public void run() {
        while (true) { //this is just an example so we don't care about stopping
            processItem(); //process a single item
            itemsProcessed++;
        }
    }
}

Now others can query your thread's progress without extra synchronization but only the thread itself is allowed to update it. For everyone else the field is read-only.

Without volatile other threads may not see any change to itemProcessed at all, or may even see them in a wacky order, sometimes increasing, sometimes decreasing.


The only time multiple threads are okay to write to a volatile variable without any extra synchronization is if the writes are idempotent (that is, multiple writes have the same effect as a single one). This can be seen in this pattern used to stop a thread:

public class Foo implements Runnable {
    private volatile boolean stopped = false;

    public void stopProcessing() {
        stopped = true;
    }

    public int getItemsProcessed() { return itemsProcessed; }

    @Override
    public void run() {
        while (!stopped) { 
            processItem(); //process a single item
        }
    }
}

Here the only thing other threads can do is set stopped to true. It doesn't matter how many of them do it and in what order, the result will always be the same.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • according to this post: https://stackoverflow.com/questions/3488703/when-exactly-do-you-use-the-volatile-keyword-in-java, looks like `volatile` is NOT enough for compound operation like i++ . Please can you take a look at `BadExample` in above link? โ€“ macemers Oct 31 '21 at 14:39
  • @macemers Note the key difference between the two though: I'm using this as a working example of a single thread write, multiple thread read scenario, where it is enough. They're talking about a multiple thread write scenario, where it most certainly isn't. โ€“ biziclop Nov 01 '21 at 10:09
0

The idea behind this is that to avoid data race there has to be only one writing thread and then the visibility guarantee is taken care of by Java Memory model through happens-before-relation. If there are multiple writing threads then there is no way to make the implicit compound operation atomic just by declaring the variable as "volatile"

17.4.4. Synchronization Order

A write to a volatile variable v (ยง8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).

Shailendra
  • 8,874
  • 2
  • 28
  • 37
0

I guess you had similar doubts as mine here. While only one thread does the read-modify-write operation to the volatile variable, it is true that a reading thread could read a "temporary" value of the variable; but it does not mean the program is not thread-safe. It reads the "temporary" value because the writing thread didn't write it to the memory yet. On other terms, the writing and reading threads are "seeing the same state of the variable" -- they're still perfectly synchronized.