0

I was asked at the interview what will be printed following this code:

static int x = 0;

    public static void main(String[] args) throws InterruptedException {

        Runnable task = () -> {
            for (int i = 0; i < 100; i++) {
                x++;
            }
        };

        final Thread thread1 = new Thread(task);
        final Thread thread2 = new Thread(task);

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

        System.out.println(x);

I answered that it's 200. I checked in IDE and it's right. But interviewer asked about atomic operation of x++ and why the answer is 200. Where is a trick?

  • 3
    `x` could be anything from `100` to `200`, really. It is pure coincidence that it happens to be `200` when you executed it. `x++` is **not atomic**. Hence thread A could fetch `x`, maybe `5`, get paused, thread B increments it 40 times, so `45`, gets paused, thread A is resumed and sets it to `6` instead of `46`; effectively losing `40` of your increments. – Zabuzard Feb 11 '21 at 16:12
  • 1
    When running things in parallel, you need to consider that things happens out of order. `x++` is equal to `x = x + 1`. This means 1. Read the value of `x`, 2. Add 1 to `x`, 3. Store it back in `x`. If the value gets changed by another tread after you execute step 2 but before you execute step 3, then you'll override whatever the other thread did. – Ted Klein Bergman Feb 11 '21 at 16:14
  • 2
    This is called "data race", which is really fundamental to multi-threading. Any tutorial that talks about multi-threading should mention it. If they don't, turn them in to the authorities! – Ted Klein Bergman Feb 11 '21 at 16:15
  • As soon as you scale up your example, higher counter values, possibly also add some short sleeps to it, extra threads, you increase chances of things going downhill and actually be able to observe wrong counter values. – Zabuzard Feb 11 '21 at 16:17
  • 1
    @Zabuzard are you sure that `x++` isn't atomic? Java Bytecode contains an in-place increment instruction (`iinc`) that should be just a single operation (therefore atomic), which I'd assume gets used here. – Green Cloak Guy Feb 11 '21 at 16:17
  • 2
    @GreenCloakGuy Even if current `javac` has it atomic (I dont know), it would not be within the JLS specifications for `++`. See https://stackoverflow.com/questions/25168062/why-is-i-not-atomic for more details. Note that just because the Bytecode has a single operation, it does not mean that its atomic. It has to be a single operation on the machine instruction level as well, so after interpretation of the JVM. (Also, atomicy is not the only problem that makes above code not thread-safe, we also need a proper happens-before relationship, for example with `volatile` on the shared variable) – Zabuzard Feb 11 '21 at 16:20
  • 1
    The increment as such can be handled by the respective byte code instruction and would be atomic then. But without defining `x` as volatile, there is no guarantee that both threads will see the same `x` all the time, as that would be a requirement for having `x++` being atomic (and not even then, as the compiler may do other strange things than using `iinc` for the increment operation). But for a short test program like the sample, it may be *coincidentally* atomic; perhaps you should have a look to the generated byte code. But *coincidentally* means that you should never rely on that trait. – tquadrat Feb 11 '21 at 16:32
  • Some of these comments (e.g., @TedKleinBergman) should be _answers._ – Solomon Slow Feb 11 '21 at 16:33
  • Oops! Sorry. My mistake. – Solomon Slow Feb 11 '21 at 16:34
  • @TedKleinBergman – `x++` is – if at all – equal to `int inc(int& v) {int temp = v; v = v + 1; return temp; )` – something you cannot even code in Java. – tquadrat Feb 11 '21 at 16:58
  • 1
    @tquadrat I'm not sure what you mean. In this case, `x++` and `x = x + 1` is semantically equal as switching between them don't change semantics of your program. `x++` creates an unused temporary, but this is reasonably optimized away. I'm guessing even at the parsing phase in the AST. `x++` and a function call is not semantically equal, as a function call creates a stack frame and redirects to another part of the program. You could code a function by wrapping the integer in a class, as class instances are passed by reference, but I'm not sure what you meant by comparing those two. – Ted Klein Bergman Feb 11 '21 at 17:40
  • @TedKleinBergman – Yupp, semantically both terms are equal, but when looking on whats going on under the hood as for this question about multithreading and atomicity, looking at the semantics only is not sufficient. That the current compiler will most probably optimises `x++` to `++x` is an implementation detail and nothing you can rely on. Also whether `x++` will be inlined or is translated to a function call. The spec is silent about that. – tquadrat Feb 11 '21 at 18:35
  • @tquadrat Yes, according to the specification, you can't rely on them being compiled to the same instructions. However, I'd like to believe that most compilers will optimize semantically equal things to their best performing instruction equivalent, especially for such a commonly occurring pattern. But it's true that they might technically differ and that it isn't something you can rely on. – Ted Klein Bergman Feb 11 '21 at 19:36
  • @TedKleinBergman – Unfortunately, believers belong to churches. I am with you, every modern compiler *should* optimise semantically equivalent code to the same opcodes, but that's no hard requirement, just a wish; the front compiler may even decide to leave the optimisation with the JIT – with the possible result that atomicity may change during runtime in cases it was not guaranteed at all. – tquadrat Feb 12 '21 at 08:31

0 Answers0