4

Recently I got an interview test and fumbled on this question. I will put what I explained and what I want to know what is the correct behavior. I want to ensure that my understanding is correct , not for the sake of interview but to get better.

**Que:**We have a counter below( java code) and if say 20 threads run this code in parallel then will the value of a and b will be same? What if b Is not valiatile

My Answer: may or may not be. Why - when there is a race condition then no guarantee that different threads will see updated values of b or a and in that case values would be different. Here, I don't think volatile makes any difference.

I wrote a simple client and ran this code with number of threads upto 50 on 16 core laptop and I could see same values for a and b when tried run for 500 times. However, when I increased number of threads to 200 then sometimes I saw different values.

Question - what is the right answer to this and is my understanding correct? And why did I see different results on my laptop?

public class VolatileCounter implements Runnable {


    private static int a;
    private volatile static int b;

    public VolatileCounter() {
    }

    @Override
    public void run() {
        try{

            b++;
            a++;
        }finally {
                      //some system println....
        }

    }

    public void printNumbers() {
        System.out.println(b);
        System.out.println(a);
    }

}
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
user3808335
  • 183
  • 1
  • 12

3 Answers3

1

The value may be different even with 50 threads, all depends on race condition and context switch. Since a and b are of primitive type, it doesnt matter whether it is volatile or not

Java Guru
  • 466
  • 4
  • 12
1

Most of the problem is that the increment operators aren't threadsafe due to taking multiple steps: first they have to get the current value, then increment, then update the field with the new value, giving other threads the opportunity to interleave operations and jumble up the results. See Is the pre-increment operator thread-safe?. (This would be the main point I'd hope to hear from someone I was interviewing if I showed them something like this.)

Some observations:

  • Just because a concurrency problem can happen doesn't mean it necessary will happen. Ramping up the concurrency level does give more chances for problems, which is what you observed.

  • This is not to say memory-visibility doesn't play a part, it's just hard to disentangle it from the un-threadsafe operator behavior in this example. Without the volatile keyword here, whether updates are visibility is at the mercy of the JVM implementation. JVMs on the PC are usually fairly forgiving. Other platforms may not be.

  • If you include printlns in your code, those acquire a lock on the console so they can affect multithreading behavior and visibility of updates.

  • There is a memory-visibility trick called piggybacking that may also come into play and affect whether updates to your class variable a are visible, see: Volatile piggyback. Is this enough for visiblity?

Community
  • 1
  • 1
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • Nathan, very fair point regarding 3 steps on increment operation and I mentioned what Peter mentioned that increment operation here is actually a load, increment and store. Thanks for adding detailed points – user3808335 Feb 18 '16 at 22:37
1

If you started 50 thread like this, starting the threads takes so long that there is a good chance they wouldn't run at the same time. If they did happen to run at the same time, you might find the numbers are slightly less than expected in either case.

Note: using a volatile field doesn't just affect the field b but it also affects all memory accesses before after after the b i.e. it also affects a.

The increment operation is actually a load, increment and store, so it is not actually thread safe.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Peter, That is very good point about thread not starting at same time. To avoid that I wrote a test using count down latch to make sure that increment operation for a, b does not happen until all thread are good to go. And i got same value for a and b. Probably a matter of race condition and then values for a, b would be different. Thanks again for your reply. – user3808335 Feb 18 '16 at 22:27