0

In java concurrency in practice, the author presents these 2 classes:

enter image description here

I don't understand why the first one is not thread-safe. He talks about the possibility of stale data, what is stale data in this situation and how do you reproduce a stale data situation here?

I don't understand what could go wrong here when using the MutableInteger class from multiple threads.

Coder-Man
  • 2,391
  • 3
  • 11
  • 19
  • Java primitives/objects aren't thread-safe, so if multiple threads access `value` at the same time you can experience a whole range of issues - see the question I've flagged this as a possible duplicate of for a list of some of them, as well as a link to a tutorial on thread-safety (though you can find a tutorial within a few seconds of searching). – hnefatl May 14 '18 at 21:50
  • @hnefatl sorry, but I already know what is talked about in that link, but it still doesn't answer my question. I want a concrete answer, and not something along the lines of "you can experience a whole range of issues" without proof. – Coder-Man May 14 '18 at 21:53
  • It's mentioned in an answer to that question: "*The memory model doesn't guarantee that you'll see the latest updates from one thread in another thread*". If one thread writes and another thread reads just after, there's no guarantee that the 2nd thread will see the "new" value. It's also almost impossible to make a reproducible example as this is entirely timing-dependent. – hnefatl May 14 '18 at 21:55
  • @hnefatl okay, why is that? I understand the examples with increment/decrement and other read-modify-write or check-then-act operations, but in this case I don't understand. – Coder-Man May 14 '18 at 21:57
  • There's a reasonable explanation [here](https://stackoverflow.com/questions/20738642/stale-value-of-shared-variable) that puts it down to the hardware, but the standard "throw-the-book" answer would be that it's simply because the language's standard doesn't guarantee it, so the implementation doesn't have to provide it. – hnefatl May 14 '18 at 21:59
  • @POrekhov, the problem is that the `MutableInteger` class makes no provision for one thread to reliably convey information to a different one. That is, if thread T1 invokes the `set()` method and thread T2 invokes the `get()` method an arbitrary number of times, there is no reason to expect T2 *ever* to see the update written by T1. – John Bollinger May 14 '18 at 22:00
  • @JohnBollinger but why is there no reason to expect that? I wanna know what happens underneath that makes the jvm behave this way. – Coder-Man May 14 '18 at 22:02
  • 1
    @POrekhov, the only real answer is that the Java Language Standard expressly disclaims any such requirement. I foresee you asking why the JLS would do so, but this is a much broader and deeper topic than is appropriate for an SO question. It is worth noting, however, that in this regard, Java behaves similarly to a wide variety of other programming languages that support multiprocessing. – John Bollinger May 14 '18 at 22:13
  • java is an api that is expected to run on multiple architectures. it tries to minimize the restrictions it puts on threads in order that implementations can run as efficiently as possible. the more the language spec enforces things being kept up to date across threads, the slower everything will be. – Nathan Hughes May 14 '18 at 22:38

1 Answers1

1

The problem is that by default, the JVM is allowed to perform a lot of tricks when working with multiple threads. It may e.g. reorder statements, duplicate variables across threads and other things.

What can go wrong in your MutableInteger:

  • Thread 1 calls set(5)
  • Thread 2 calls set(3)
  • Thread 1 calls get() and receives 5 (not 3) as the result, even though Thread 2 has finished calling set(3) on it.

How is SynchronizedInteger better? Entering a synchronized method (or block) forces the JVM to do two things:

  • only one thread may enter at a time, all others have to wait (exclusive lock) AND
  • all changes to the synchronization target are made visible to all threads immediately when the block is exited

So in the example above, Thread 1 will receive 3 when calling get(), not 5 as before.

By the way, for primitives (int, char, float...) you can use the volatile keyword to force changes to be visible to all threads immediately. Alternatively, use the builtin AtomicInteger and friends for better performance than synchronized methods.

Martin Häusler
  • 6,544
  • 8
  • 39
  • 66
  • Oh, so it's the reordering, right? I can do set(5), set(3), get(), but the jvm will reorder that sequence to set(5), get(), set(3)? – Coder-Man May 14 '18 at 22:00
  • It's not only reordering. Depending on your JVM, the field `value` can temporarily exist once per thread. So you have multiple copies and only the owning thread can see its changes. All others have their own copy. It all depends on the timing. It CAN work without synchronization. But it is NOT reliable. – Martin Häusler May 14 '18 at 22:01
  • Why would it makes copies? I tought that state is shared. Like all threads share the same heap but different stack, if MutableInteger was allocated on the heap, how could there be copies in different threads? – Coder-Man May 14 '18 at 22:05
  • The state is shared. But *temporarily* the JVM is allowed to make copies of individual variables. It's mostly for performance reasons. If you synchronize your code properly, you will never notice it. Look up the exact meaning of the `volatile` keyword if you are interested; essentially it prevents the JVM from copying the variable. – Martin Häusler May 14 '18 at 22:08
  • General word of advice: never actually use `volatile`. Use an `AtomicInteger` or related classes, or synchronized blocks, or explicit locks. – Martin Häusler May 14 '18 at 22:10
  • Thanks for explaining, I will look it up. So, how does synchronized guarantee memory visibility? Some jvm algorithm looks at variables used in synchronized blocks when compiling your code and makes them volatile for you, or what? – Coder-Man May 14 '18 at 22:11
  • I'm sorry but I don't know the precise details about what synchronized does under the hood with respect to variable visibility. My guess is that upon closing of a synchronized block, all performed changes get flushed to all other threads, thus making them visible. While it is interesting to dive into such topics, keep in mind that these things may change from one JVM to another, and/or from one version to the next. Never ever rely on them while coding in Java. – Martin Häusler May 15 '18 at 12:14