1

I need some help to fully understand what's happening when, running this code

public class Main extends Thread {

    private static int x;

    public static void main(String[] args) {
        Thread th1 = new Main("A");
        Thread th2 = new Main("B");
        th1.start();
        th2.start();
    }

    public Main(String n) {
        super(n);
    }

    public void run() {
        while(x<4) {   //1
            x++;       //2
            System.out.print(Thread.currentThread().getName()+x+" ");   //3
        }
    }
}

I get the output

B2 B3 B4 A2 

I understand that threads A and B both increment x, then B loops incrementing and outputting... but why is last output A2? Shouldn't A see x as 4 when executing //3?

Bonus question: why is it impossible for x to become 5?

EDIT

This question (in a slightly different form) comes from a mock test for OCP certification, where explanation states that x will never be 5. I'm glad to see that I'm not the only one to disagree.

Luigi Cortese
  • 10,841
  • 6
  • 37
  • 48

5 Answers5

5

When you update a variable's value in one thread, its value is not necessarily visible to all threads immediately. This is because memory is held in the CPU cache, which allows it to be read and written much more quickly than it would be to main memory.

Periodically, the updated contents of the cache are copied to main memory. It is only when this happens that other threads see updates to values.

What it looks like is happening here is that B is updating the value, but that value is not being committed to main memory; as such, A sees old values of it.

If you make the variable volatile, all reads and writes are done directly from/to main memory (or, at least, the cache is refreshed from/flushed to main memory), so updates to the values are visible immediately to all threads.

Note, however, that you are not performing atomic reads and writes: it is possible for another thread to update the value of x in between the current thread checking x < 4 and incrementing x++. As such, you might end up with a value of 5 being printed.

The easiest way to fix this is to make the checking/incrementing synchronized:

synchronized (Main.class) {
  if (x < 4) {
    x++;
    System.out.println(...);
  }
}

This also has the effect of ensuring visibility of updates to x in all threads, but also ensures that only one thread can check/increment x at once.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Reading the answer from (the other) Andy, I just made `x` `static volatile` but got the output `A2 A3 A4 B2`. Looks like `volatile` is not the key point here... am I wrong? – Luigi Cortese Jan 12 '16 at 14:42
3

This is a classic race condition. When you call th1.start() & th2.start() it only schedules the thread to start, it doesn't sequentially start then and there. As a result, your actual threads can and do start in any old order. Now, add to that fact that between while (x<4) or x++ or System.out.println any one of the threads can schedule out and allow another thread to run and you basically get undefined behavior.

Bonus question: why is it impossible for x to become 5?

It's not impossible (for the same reason the output is interleaved). Try increasing your number of threads and eventually you'll see x become 5 and maybe even higher depending on how much thread contention you can create.

I disagree with others that this is a volatility issue. Rather this is a shared memory access issue. Using volatile alone will not fix this. A simple mutex around the static x variable access will properly protect it and sequence how you expect with the exception of the order of 'A' vs. 'B' which would require additional synchronization.

Andy
  • 1,663
  • 10
  • 17
  • Do you mean that using `volatile` alone won't guarantee the `x` value to be incremented by one at every `print()`? – Luigi Cortese Jan 12 '16 at 14:33
  • Yes. Using `volatile` alone will not guarantee that. Even with `volatile` you can still easily have `B1 B3 A4` etc. – Andy Jan 12 '16 at 14:35
  • Damn, but I wasn't expecting `B2 B3 B4 A2` after adding `volatile`... How can `A` print 2 if previous output is 4?? – Luigi Cortese Jan 12 '16 at 14:46
  • You need to also add the `synchronized` block as recommended by @AndyTurner. This is the same as my recommendation for a mutex but using core java instead of java libraries. – Andy Jan 12 '16 at 14:48
  • But if `x` is `volatile`, how can the 4th output be `2` if the third is `4`? Without cache copies, I thought you could expect the value to be incremented more than once before being printed, but not decreased! Am I being clear...? – Luigi Cortese Jan 12 '16 at 14:53
  • 1
    @LuigiCortese Because System.out.println() also has synchronization built into it. Also, thread scheduling can be a nasty beast. The value A2 is read long before B makes any changes, then A is blocked or descheduled. – Erik Nyström Jan 12 '16 at 14:57
  • @ErikNyström so, if `print()` were an atomic step, I'd have an always bigger `x`... I suppose. – Luigi Cortese Jan 12 '16 at 15:01
  • Unfortunately, `System.out.println` can not be treated as thread-safe (see http://stackoverflow.com/questions/9459657/synchronization-and-system-out-println). Thus you need to synchronize yourself with the synchronized block which assures (in connection with `volatile`) that all operations are performed before any thread sees the changes. @AndyTurner is right in that *both* are needed. – Andy Jan 12 '16 at 15:04
  • 1
    @Andy Sorry for pinging you. Just realized my own foolishness. – Erik Nyström Jan 12 '16 at 15:11
  • @ErikNyström No worries Erik, I wouldn't call it foolish - we're all learning here. LuigiCortese I'd recommend accepting AndyTurners answer, it's the most complete. – Andy Jan 12 '16 at 15:24
2

You, my friend, have run into what is called a Data Race.

Wikipedia has an example depicting exactly what you are going through: https://en.wikipedia.org/wiki/Race_condition.

So, why is this happening? The reason lies hidden in the way a computer process instructions. Take, for example, the following line of java code:

x++;

Now, ignoring compiler magic for the moment, we have to think what the computer needs to do to execute this instruction.

  1. We need to read the old value of x.
  2. We need to perform the addition x + 1.
  3. We need to write the new value back into the variable x.

This works wonderfully when just looking at it from a sequential standpoint. But what happens if two people are doing the exact same thing, at the same time?

See the Wikipedia example for exact answers.

The important thing to note here is that your single x++ instruction is actually multiple instructions for a computer. Even if each instruction can be carried out atomically by the processor, you are not guaranteed atomicity for the whole sequence of instructions.

The same holds true for using the variable x. When you are calling the System.out.println() function, you are once again accessing x. This access means that we have to read x from memory again.

Do we know what B has done to the variable from the time you changed it? Nope.

Also, I noticed the volatile comment. This is actually wrong (as confirmed by running the code on my computer). volatile ensures that we do not read/write jumbled data into the variable. It does not ensure any other atomicity.

Bonus question: why is it impossible for x to become 5?

It is very possible, although perhaps unlikely. The part of your program that takes time is the work and synchronization done inside your System.out.println() statement. This is probably why you do not see the value 5 often.

Erik Nyström
  • 537
  • 4
  • 9
  • Thanks for being friends! =) Anyway, at this point I'm really confused about what `volatile` really does... – Luigi Cortese Jan 12 '16 at 14:56
  • @LuigiCortese Sorry, didn't mean to be so gruff. Indeed the volatile keyword, it is fairly obscure. Generally, you are never guaranteed that you even read sensible data when reading a variable. you may read/write some half-updated value that is horribly wrong (say 100483 in the current example). The volatile keyword would ensure that this does not happen, and that you always read the value instead of storing it in registers. Then again, most PC computers these days run with a memory model which actually guarantee such reads/writes never happen. I do not know if the same apply to servers. – Erik Nyström Jan 12 '16 at 15:05
  • You've not been "gruff"! New word added to my English vocabulary! – Luigi Cortese Jan 12 '16 at 15:20
0

Your variable x is static so it is shared between both threads.

Thread B increments x to 4 and completes, writing each step as it goes.

Thread A gets one chance to look at x when it is at 1 so increments it and prints A2. The next time it sees x it is at >= 4 so it exits its loop.

Bonus question - yes it is possible for x to become 5 - and even print as 5. If both threads check x<4 when it happens to be 3 at the same time they will both increment it.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
0

knowing that start is asynchronous method call so first one off the thread will start before the other. two : x is a static but in a local context means the first running thread will change the x while the second is still sleeping (when sleeping the second have a local stored value of the local static x that he will use once he awaken ) after that once the second thread print the local x he will seek it on the memory(the global one) and find it equals to 4 so he will stop.

this may help

|------------------------------------------------------------------------------------------|

| Thread A:x works |local| big static X that changed . . . . . . . . . . . . . . ..|

| Thread B:x=2 sleep |local| big static X that will be read after first loop.|

|------------------------------------------------------------------------------------------|

so we can say x is local and global in the same time

proof : add a sleep with random time and see the result for x<10 after the increment dont forget the try catch clause.