0

I have two threads doing calculation on a common variable "n", one thread increase "n" each time, another decrease "n" each time, when I am not using volatile keyword on this variable, something I cannot understand happens, sb there please help explain, the snippet is like follow:

public class TwoThreads {

private static int n = 0;
private static int called = 0;

public static void main(String[] args) {

    for (int i = 0; i < 1000; i++) {
        n = 0;
        called = 0;

        TwoThreads two = new TwoThreads();
        Inc inc = two.new Inc();
        Dec dec = two.new Dec();
        Thread t = new Thread(inc);
        t.start();
        t = new Thread(dec);
        t.start();
        while (called != 2) {
            //System.out.println("----");
        }
        System.out.println(n);

    }
}

private synchronized void inc() {
    n++;
    called++;
}

private synchronized void dec() {
    n--;
    called++;
}

class Inc implements Runnable {
    @Override
    public void run() {
        inc();
    }
}

class Dec implements Runnable {
    @Override
    public void run() {
        dec();
    }
}

}

1) What I am expecting is "n=0,called=2" after execution, but chances are the main thread can be blocked in the while loop;

2) But when I uncomment this line, the program when as expected:

//System.out.println("----");

3) I know I should use "volatile" on "called", but I cannot explain why the above happens;

4) "called" is "read and load" in working memory of specific thread, but why it's not "store and write" back into main thread after "long" while loop, if it's not, why a simple "print" line can make such a difference

3
  • 1
    I think you can get the same effect using Thread.sleep() method instead of the print(). Commented Aug 30, 2016 at 12:41
  • @victor you are right, any line can have the same effect, cannot explain why Commented Aug 30, 2016 at 12:45
  • Well, you don't control when any thread is execute; but the main will continue executing and by the time it finish it the other thread will too. That's why you should use Thread.join() to stop the main until the rest of the thread can complete. (Thread.sleep() will do it for a amount of time but join() will wait until they finnish) Commented Aug 30, 2016 at 12:52

3 Answers 3

2

You have synchronized writing of data (in inc and dec), but not reading of data (in main). BOTH should be synchronized to get predictable effects. Otherwise, chances are that main never "sees" the changes done by inc and dec.

Sign up to request clarification or add additional context in comments.

1 Comment

I'll need to see your entire code for this, and also there are lot of different approaches for this. Basically, put your reading and writing code in synchronized blocks and use a common lock object. Since main is static method, if you want to read there, you can only use the class object or a static class-level variable. For eg: synchronized(TwoThreads.class) { //read n and/or called here } synchronized(TwoThreads.class) { //write n and/or called here }
0

You don't know where exactly called++ will be executed, your main thread will continue to born new threads which will make mutual exclusion, I mean only one thread can make called++ in each time because methods are synchronized, and you don't know each exactly thread will be it. May be two times will performed n++ or n--, you don't know this, may be ten times will performed n++ while main thread reach your condition.

and try to read about data race

 while (called != 2) {
            //System.out.println("----");
 }

//.. place for data race, n can be changed

 System.out.println(n);

2 Comments

thanks serega, I expect there will be at most two threads(inc and dec) running except main thread, because otherwise it can be blocked in while loop, I loop 1000 times only to make see that there is possibility it can be stucked.
You can't be sure that while loop will block, use called > 2 for that purpose, because your threads can make called++ 1000 times while your main thread reach while loop) and you can't to be sure anyway, because main thread can reach while loop before inc and dec threads make called++ and condition will fail, and you will get another pair of new threads
0

You need to synchronize access to called here:

while (called != 2) {
    //System.out.println("----");
}

I sugest to add getCalled method

private synchronized int getCalled() {
    return called;
}

and replace called != 2 with getCalled() != 2

If you interested in why this problem occure you can read about visibility in context of java memory model.

2 Comments

talex thanks, I will google that, but why "System.out.println("----");" can get the job done, and when the data will be sync back....
Because it have synchronization inside and this synchronization flush some caches. There is no guarantee that this will not be changed in future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.