xmcax
xmcax

Reputation: 391

Handling state in an instance that is used by multiple threads

Let's consider following mutable Java class (taken from "Java Concurrency in Practice" by Brian Goetz):

@ThreadSafe
public class SynchronizedInteger {
  @GuardedBy("this") private int value;

  public synchronized int get() { return value; }
  public synchronized void set(int value) { this.value = value; }
}

Assuming that this class has a single instance that is used by multiple threads, which of the following methods added to the class does NOT break its thread-safety?

Solution #1:

public boolean isPositive() { return value > 0; }

Solution #2:

public synchronized boolean isPositive() { return value > 0; }

Solution #3:

public boolean isPositive() { return get() > 0; }

My understanding is that, in order to ensure visibility, I need to make sure that the field value is properly refreshed in the CPU/core cache, so Solution #2 and #3 are the way to go. Can anyone confirm this?

Second question: Let's assume we have a Java Spring singleton that has some state held in a private field. This singleton is used by some request-scoped beans. Now, each request has it's own thread (with it's own call stack, but shared heap). Since the state of the singleton resides on the heap, any method defined in the singleton should somehow synchronize access to it's private member (= its state) in order to ensure the visibility, e.g. using lock, volatile, ReentrantLock, etc. Do I get it right?

Upvotes: 1

Views: 165

Answers (1)

Solomon Slow
Solomon Slow

Reputation: 27190

What does "thread-safe" really mean?

Any programmer, looking at any class, is going to have expectations about how the class will behave. To me, "thread-safe" means that if you call its methods from multiple threads, you should not have to change your expectations.

Instead of saying, "SynchronizedInteger is thread-safe," I'd rather be explicit about what the guarantees are, and in this case, they're pretty easy to sum up: It acts like a volatile.

Your "solution #2" and your "solution #3" both preserve that guarantee. Your solution #1 doesn't.


Let's assume we have a [shared object]. Any method defined [on] the [shared object] should somehow synchronize access to it's [state] in order to ensure the visibility [...] Do I get it right?

Yes. That's a start, but it may not be enough. If the state is complex (i.e., if it depends on relationships between two or more variables) then it may be the case that some operation that changes the state requires several steps, and it may be the case that when it's half way through the change, the state will be invalid.

In that case you need to use mutual exclusion (e.g., synchronized blocks) to ensure that no other thread except the one that is making the change will ever see the invalid state. That means you not only need to throw a mutex around every bit of code that could create an invalid state; You must also throw a mutex around every bit of code that must be protected from seeing the invalid state.

Upvotes: 3

Related Questions