2

Firstly let me say that I am aware of this being a fairly common topic here but searching for it I couldn't quite find another question that clarifies the following situation. I am very sorry if this is a possible duplicate but here you go:

I am new to concurrency and have been given the following code in order to answer questions:

  • a) Why any other output aside from "00" would be possible?
  • b) How to amend the code so that "00" will ALWAYS print.
 boolean flag = false;

    void changeVal(int val) {
        if(this.flag){
            return;
        }
        this.initialInt = val;
        this.flag = true;
    }

    int initialInt = 1;

    class MyThread extends Thread {
        public void run(){
            changeVal(0);
            System.out.print(initialInt);
        }
    }

    void execute() throws Exception{
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start(); t2.start(); t1.join(); t2.join();
        System.out.println();
    }

For a) my answer would be the following: In the absence of any volatile / synchronization construct the compiler could reorder some of the instructions. In particular, "this.initialInt = val;" and "this.flag = true;" could be switched so that this situation could occur: The threads are both started and t1 charges ahead. Given the reordered instructions it first sets flag = true. Now before it reaches the now last statement of "this.initialInt = val;" the other thread jumps in, checks the if-condition and immediately returns thus printing the unchanged initialInt value of 1. Besides this, I believe that without any volatile / synchronization it is not for certain whether t2 might see the assignment performed to initialInt in t1 so it may also print "1" as the default value.

For b) I think that flag could be made volatile. I have learned that when t1 writes to a volatile variable setting flag = true then t2, upon reading out this volatile variable in the if-statement will see any write operations performed before the volatile write, hence initialInt = val, too. Therefore, t2 will already have seen its initialInt value changed to 0 and must always print 0. This will only work, however, if the use of volatile successfully prevents any reordering as I described in a). I have read about volatile accomplishing such things but I am not sure whether this always works here in the absence of any further synchronized blocks or any such locks. From this answer I have gathered that nothing happening before a volatile store (so this.flag = true) can be reordered as to appear beyond it. In that case initialInt = val could not be moved down and I should be correct, right? Or not ? :)

Thank you so much for your help. I am looking forward to your replies.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
Dbadtf_385
  • 31
  • 1
  • 5

2 Answers2

1

This example will alway print 00 , because you do changeVal(0) before the printing .

to mimic the case where 00 might not be printed , you need to move initialInt = 1; to the context of a thread like so :

class MyThread extends Thread {
        public void run(){
            initialInt = 1;
            changeVal(0);
            System.out.print(initialInt);
        }
    }

now you might have a race condition , that sets initialInt back to 1 in thread1 before it is printed in thread2

another alternative that might results in a race-condition but is harder to understand , is switching the order of setting the flag and setting the value

void changeVal(int val) {
        if(this.flag){
            return;
        }
        this.flag = true;
        this.initialInt = val;
 }
Shachaf.Gortler
  • 5,655
  • 14
  • 43
  • 71
  • Thank you. But will this switch only ever appear when being explicitly coded or could it also be the result of the compiler reordering expressions in the absence of synchronisation features like volatile. Then, the question arises whether using a volatile flag variable would completely rule out the possibility of (^) this reordering happening nonetheless – Dbadtf_385 Jul 29 '20 at 21:18
  • No to synchronize thread fully in an expandable way , you need to use hight level constructs such critical section , mutex etc . volatile might work in this simple case when you only have 1 common variable . – Shachaf.Gortler Jul 29 '20 at 21:30
1

There are no explicit synchronizations, so all kinds of interleavings are possible, and changes made by one thread are not necessarily visible to the other so, it is possible that the changes to flag are visible before the changes to initialInt, causing 10 or 01 output, as well as 00 output. 11 is not possible, because operations performed on variables are visible to the thread performing them, and effects of changeVal(0) will always visible for at least one of the threads.

Making changeVal synchronized, or making flag volatile would fix the issue. flag is the last variable changed in the critical section, so declaring it as volatile would create a happened-before relationship, making changes to initialInt visible.

Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thanks for the answer. So am I right to assume that volatile does indeed rule out the possibility of a reordering between this.initialInt = val (lets call this assignment x); and this.flag = true (y);? This switch would have been possible in the absence of volatile (aside from the other interleavings you mentioned), correct? So just to clarify: If I added an additional assignment z before x even with a volatile flag, the compiler could still switch z and x arbitrarily, however, it could not move either z or x beyond y, is this true? – Dbadtf_385 Jul 29 '20 at 21:12
  • This is good info about volatile and should explain how things behave: http://tutorials.jenkov.com/java-concurrency/volatile.html – Burak Serdar Jul 29 '20 at 21:20