Code snippet - 1
class RequestObject implements Runnable
{
private static Integer nRequests = 0;
@Override
public void run()
{
synchronized (nRequests)
{
nRequests++;
}
}
}
Code snippet - 2
public class Racer implements Runnable
{
public static Boolean won = false;
@Override
public void run()
{
synchronized (won)
{
if (!won)
won = true;
}
}
}
I was having a race condition with the first code snippet. I understood that this was because I was obtaining a lock on an immutable object(of type Integer).
I have written a second code snippet which is again impervious to 'Boolean' being immutable. But this works(no race condition is displayed in an output run). If I have understood the solution to my previous question properly the below is is one possible way in which things can go wrong
won
won
and gets in the wait queue for Awon = true
(A thinks it won the race). won
)false
(immutably so). It now goes into the synchronized block and assumes that it has also won, which is not correct.Why is the second code snippet working fine all the time??
Since immutable object's state doesn't change, they can be used in concurrent programming without any need for synchronization.
Immutable objects are inherently thread-safe. They do not require synchronization.
Immutable classes make sure that values are not changed in the middle of an operation without using synchronized blocks. By avoiding synchronization blocks, you avoid deadlocks. And since you are always working with an unchangeable consistent state, you avoid race conditions.
That's because each thread changes the reference to the object that is to be locked. In order to synchronize properly, all of the threads need to lock the same object; e.g.
synchronized (won)
{
if (!won)
won = true;
}
Here you have a transient race condition which you don't notice because it goes away after the first execution of the run
method. After that the won
variable constantly points to the same instance of Boolean
representing true
, which thus serves properly as a mutex lock.
This is not to say that you should ever write such code in real projects. All lock objects should be assigned to final
variables to make sure they never change.
Whether or not an object is immutable has nothing to do with whether it's suitable as lock object in a synchronized
statement. It is important, however, that the same object be used by all threads entering the same set of critical regions (hence it may be wise to make the object reference final
), but the object itself can be modified without affecting it's "lockiness". Additionally, two (or more) different synchronized
statements can use different reference variables and still be mutually exclusive, so long as the different reference variables all refer to the same object.
In the above examples the code in the critical region replaces one object with another, and that is a problem. The lock is on the object, not the reference, so changing the object is a no-no.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With