0

I'm currently trying to attempting to study concurrency, specifically "volatile" keyword.

By declaring the counter variable volatile all writes to the counter variable will be written back to main memory immediately. Also, all reads of the counter variable will be read directly from main memory. Here is how the volatile declaration of the counter variable looks

and

When a thread writes to a volatile variable, then not just the volatile variable itself is written to main memory. Also all other variables changed by the thread before writing to the volatile variable are also flushed to main memory. When a thread reads a volatile variable it will also read all other variables from main memory which were flushed to main memory together with the volatile variable.

Source : tutorials.jenkov.com | Java Concurrency - Java Volatile Keyword

Which makes me conclude/assume that any change that i make to a volatile variable will always be visible to all thread. So, I make a code to test it.

TestClass

package org.personal.test1;

class TestClass {
    public static int w = 0;
    public static int x = 0;
    public static int y = 0;
    public static volatile int z = 0;
    private static final int ITERATIONS = 100000;


    public static void sooPlus(int indents) {
        for (int i = 0; i < TestClass.ITERATIONS; i++) {
            TestClass.w++;
            TestClass.x++;
            TestClass.y++;
            TestClass.z++;
        }
    }

    public static void sooMinus(int indents) {
        for (int i = 0; i < TestClass.ITERATIONS; i++) {
            TestClass.w--;
            TestClass.x--;
            TestClass.y--;
            TestClass.z--;
        }
    }


    public static synchronized String getVariableValues () {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("(");
        stringBuilder.append("w : "+TestClass.w+", ");
        stringBuilder.append("x : "+TestClass.x+", ");
        stringBuilder.append("y : "+TestClass.y+", ");
        stringBuilder.append("z : "+TestClass.z+")");
        return stringBuilder.toString();
    }

}

Main Class

package org.personal.test1;

/**
 * <ol type="I">
 *     <li>
 *         <a href="http://tutorials.jenkov.com/java-concurrency/volatile.html">jenkov.com - Java Volatile Keyword</a>
 *     </li>
 * </ol>
 */
public class Main {

    public static void main(String[] args) {
        Main.call1();
    }

    private static void call1() {
        Main.test1();
    }

    private static void test1() {
        Thread thread1 = new Thread("Thread1") {
            @Override
            public void run() {
                TestClass.sooPlus(1);
            }
        };

        Thread thread2 = new Thread("Thread2") {
            @Override
            public void run() {
                TestClass.sooMinus(4);
            }
        };

        thread1.start();
        thread2.start();

        try {
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(TestClass.getVariableValues());

    }
}

The results that i get were not what i was expecting.

What i get (varies)

(w : -2314, x : -1692, y : -1416, z : -1656)

What I'm expecting

(w : 0, x : 0, y : 0, z : 0)

or at least

(w : -2314, x : -1692, y : -1416, z : 0)

The Questions

  1. What did I assume/conclude wrong that resulted in a different output than expected?
  2. Was my testing methodology incorrect? If yes, then how can i fix it?
  3. (optional) Are there any good tutorial on Java Concurrency that you recommend?

Notes

  • I did attempt to read similar questions but i wasn't able to fully understand what the questioner was attempting to do in order to understand his problem.
  • 1
    The volatile keyword provides a weak form of thread safety. It guarantees visibility, but not atomicity or mutual exclusion. Is it possibile for a volatile field to be not thread safe? Yes, if the writes to the field are not atomic. Is it possible for a class made up of all volatile fields to be not thread safe? Yes, if writes to the fields cause invalid state transitions (by lack of synchronization). – scottb Apr 23 '16 at 18:12
  • [`AtomicInteger`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/atomic/AtomicInteger.html) is your friend here. See my answer on similar question, [*How can I synchronize two threads in Java*](https://stackoverflow.com/q/66136163/642706) – Basil Bourque Feb 10 '21 at 23:06

1 Answers1

3

The volatile keyword provides a weak form of thread safety. It guarantees visibility, but not atomicity or mutual exclusion.

  • Is it possibile for a volatile field to be not thread safe? Thread safe. Writes to non-volatile double and long values are not atomic, but writes to volatile double and long variables are.

  • Is it possible for a class made up of all volatile fields to be not thread safe? Yes, if writes to the fields cause invalid state transitions (by lack of synchronization).

(optional) Are there any good tutorial on Java Concurrency that you recommend?

The book that is usually recommended as the authoritative treatment of this subject is "Java Concurrency in Practice" by Brian Goetz. It's getting a bit old.

scottb
  • 9,908
  • 3
  • 40
  • 56
  • -1, sorry. Much of this answer is correct (especially the mention of *mutual exclusion* -- which you should elaborate on), but volatile writes *are* guaranteed to be atomic (see https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7), and this answer puts the (erroneous) contrary claim rather front-and-center. – ruakh Apr 23 '16 at 18:18
  • I stand corrected. The answer is revised. – scottb Apr 23 '16 at 18:25
  • Thanks; downvote retracted. Still, the really important point, for the OP's example code, is that something like `TestClass.w--;` consists of a read followed by a completely separate write, with the result that it doesn't necessarily actually decrement `TestClass.w`. (It's equivalent to `int tmp = TestClass.w; tmp--; TestClass.w = tmp;`, meaning that it can supersede/overwrite/discard other concurrent changes to `TestClass.w`.) – ruakh Apr 24 '16 at 01:03