Reputation: 4582
I was reading though a book on Java and there was this exercise question where they declared a class with one private variable, one public void
method that did some expensive operation to calculate and then set the private variable, and a second public method to return the private variable. The question was "how can you make this thread-safe" and one possible answer was "synchronize each of the two methods" and one other possible answer was "this class can not be made thread-safe".
I figured the class could not be made thread-safe since even if you synchronize both methods, you could have a situation that Thread1 would invoke the setter and before Thread1 could invoke the getter, Thread2 might execute and invoke the setter, so that when Thread1 went and retrieved the result it would get the wrong info. Is this the right way to look at things? The book suggested the correct answer was that the class could be made thread safe by synchronizing the two methods and now I'm confused...
Upvotes: 1
Views: 165
Reputation: 1278
I figured the class could not be made thread-safe since even if you synchronize both methods, you could have a situation that Thread1 would invoke the setter and before Thread1 could invoke the getter, Thread2 might execute and invoke the setter, so that when Thread1 went and retrieved the result it would get the wrong info. Is this the right way to look at things?
You are correct with this. There is no way to guarantee that a thread will not have called either of the methods in between your calls of each of the methods, from within the class.
If you do want to do this, that will require a wrapper class. So if the class with the getter and setter is like so:
class Foo
{
private static int bar;
public static synchronized void SetBar(int z) { ... }
public static synchronized int GetBar() { ... }
}
The wrapper class would look something like this:
class FooWrapper
{
public synchronized int SetGetBar(int z)
{
Foo.SetBar(z);
return Foo.GetBar();
}
}
The only way to guarantee this will work is if you can guarantee that all calls will go through your wrapper class rather than directly to class Foo.
Upvotes: 3
Reputation: 5187
When you make those two synchronized, the getter and setter themselves are thread-safe. More specifically:
However, making the getter and setter themselves thread-safe does not mean that the application as a whole (i.e. whatever is using this class) is thread-safe. If your thread wants to call a setter then get the same value upon invoking the getter, that involves synchronization on a different level.
As far as thread-safety is concerned, a thread-safe class need not control how its methods are invoked (for example, it need not control which way the threads interleave their calls), but it needs to ensure that when they are, the methods do what they are supposed to.
Upvotes: 1
Reputation: 138171
synchronized
in Java is an object-wide lock. Only one synchronized
method of any given object can be executed on any given thread at a time. Let's have this class:
class Foo
{
private int bar;
public synchronized void SetBar() { ... }
public synchronized int GetBar() { ... }
}
SetBar()
. Thread 1 acquires the object lock.SetBar()
, but Thread 1 holds the lock. Thread 2 is now queued to acquire the lock when Thread 1 will release it.SetBar()
and releases the lock.SetBar()
.GetBar()
. Thread 1 is now queued to acquire the lock when Thread 2 will release it.SetBar()
and releases the lock.GetBar()
, and is done with it.You did the work twice, but you didn't cause any race condition. It may or may not be erroneous to do the work twice, depending on what it is.
A frequent pattern is to have one thread produce content and one other thread do something useful with it. This is called the producer-consumer pattern. In this case, there is no confusion over who or what tries to SetBar()
and what tries to GetBar()
.
Upvotes: 0