5

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

  1. Thread 1 gets a lock on an object(say A) pointed by won
  2. Thread 2 tries to now get a lock on the object pointed to by won and gets in the wait queue for A
  3. Thread 1 goes into the synchronized block, verifies that A is false and creates a new object(say B) by saying won = true(A thinks it won the race).
  4. 'won' now points to B. Thread 1 releases the lock on object A(no longer pointed to by won)
  5. Now, thread-2 which was in the wait queue of object A gets woken up and gets a lock on object A which is still 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??

Community
  • 1
  • 1
Abhijith Madhav
  • 2,748
  • 5
  • 33
  • 44
  • How is this different to your previous question? – Mitch Wheat Aug 04 '13 at 09:34
  • 3
    This code example looks similar but (seems) to behave different. A valid follow up question in my book. – Jens Schauder Aug 04 '13 at 09:55
  • I think you have misunderstood the meaning of immutable. To make something immutable you should mark it as final – luketorjussen Aug 04 '13 at 10:07
  • 4
    @luketorjussen this is not true. Immutable means you cannot change the state of the object once created. For example, there are no `set` methods that can change the object as in String, Integer, Boolean,... Being immutable has nothing to do with being `final` – Multithreader Aug 04 '13 at 10:10
  • @multithreader, but if the variable is pointing to an integer, then you change the value of that integer, the state has changed? – luketorjussen Aug 04 '13 at 10:23
  • @luketorjussen, in fact the value of the object does not change. Example, `Integer a = Integer.valueOf(4); Integer b = a; a++;` now print `a` and `b`. `a` will be 4 and `b` will be 5 – Multithreader Aug 04 '13 at 11:04
  • @multithreader - True, but the value of the integer that `nRequests` is pointing to changes, therefore `nRequests` is mutable. – luketorjussen Aug 04 '13 at 11:31
  • `I understood that this was because I was obtaining a lock on an immutable object(of type Integer)` Obviously you don't understand the problem. It is just not because of "obtaining lock on immutable obj" – Adrian Shum Aug 05 '13 at 03:51
  • 2
    A point of confusion is that implicit boxing is going on in both examples. This causes any "modification" of the variable to replace the object with a new object. Both above examples have this problem (replacing the lock object with a new one), it's just that the second example had nothing that needs synchronization (one could remove the `synchronized` statement entirely with no external effect), so the problem will never produce "unexpected" results. – Hot Licks Aug 05 '13 at 03:52
  • @luketorjussen think about it in this way. Let's say that you have a red car. Now you don't like the color and you want to change it to yellow. What are your options? 1. you can have it painted to yellow. OR 2. you can buy a new yellow car. The first option represents 'mutable' and the second option represents 'immutable'. In other words, if the first option is available to you, then you can say that the car "object" is mutable. However, if the first option is NOT available to you, then you can say that the car "object" is immutable. – Multithreader Aug 05 '13 at 08:29
  • @multithreader, the object IS immutable, but the variable is mutable. e.g. If I `wait` for a red car to appear on my driveway, I'd be waiting forever because I now own a yellow car. The object I am waiting for is not the same object I started with. – luketorjussen Aug 05 '13 at 19:28
  • @HotLicks: "...second example had nothing that needs synchronization" Are you sure? Synchronization is needed for two reasons: for mutual exclusion/serialization, and for visibility. Since the public Boolean field is not declared volatile or final, the Java Memory Model does not guarantee that other threads will be able to 'see' the most current value (it may, for example be locally cached) -unless- access to it is synchronized. As written, I believe method two is still flawed ... just in a different way. – scottb Aug 06 '13 at 01:05

4 Answers4

8
    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.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 1
    To confirm, you mean to say that there is this very small window in which things might go wrong. Between the first thread(which enters the synchronized block) testing the object pointed to by `won` and then setting it to another object. Right? After that `won` always points to a true object and hence no race condition. – Abhijith Madhav Aug 04 '13 at 09:49
  • 1
    Even if that happens, both threads will assign the exact same object to the `won` variable, so even taking that into account this could be deemed thread-safe---but *only* in the literal form you present, with no other code within the `synchronized` block. – Marko Topolnik Aug 04 '13 at 09:58
  • Hmm... I am not able to understand your clarification. Can you please tell me where exactly in the illustration(which I have provided in my question) am I analyzing wrongly? – Abhijith Madhav Aug 04 '13 at 13:44
  • 1
    In your analysis you first assign A to mean the initial `false` object, and later say "which is still `true`", but that it a side point. Other than that, your analysis is all correct, except your *expectation* that the second thread should in any scenario read `false`. That would be possible only if you used a `ThreadLocal`. – Marko Topolnik Aug 04 '13 at 14:05
  • 1
    Maybe you're missing this as well: when the second thread enters the `synchronized` block, it re-reads the current value of `won` and gets `true`. It doesn't matter that it is holding the lock of the earlier value of `won`. – Marko Topolnik Aug 04 '13 at 15:46
  • Absolutely, that was exactly what I was missing. Thanks. I get it now. By the way, I've corrected the "which is still `true`" to "which is still `false`". – Abhijith Madhav Aug 05 '13 at 03:07
2

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).

Actually, that is not the reason at all. Obtaining the lock on an immutable object, will "work" just fine. The problem is that it probably won't do anything useful ...

The real reason that the first example breaks is that you are locking the wrong thing. When you do execute this - nRequests++ - what you are actually doing is equivalent to this non-atomic sequence:

    int temp = nRequests.integerValue();
    temp = temp + 1;
    nRequests = Integer.valueOf(temp);

In other words, you are assigning a different object reference the static variable nRequests.

The problem is that in your snippet the threads will be synchronizing on a different object each time an update is made to the variable. 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.

class RequestObject implements Runnable
{
    private static Integer nRequests = 0;
    private static final Object lock = new Object();

    @Override
    public void run()
    {       
        synchronized (lock)
        {
            nRequests++;
        }
    }
}

In fact, the second example suffers from the same problem as the first. The reason you are not noticing it in your testing is that the transition from won == false to won == true happens just once ... so the probability that the potential race condition will actually eventuate is much, much smaller.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

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.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
0

In fact, your second code is also not thread safe. Please use the code below to check for yourself (you will find out that the first print statement will be 2 sometimes which means that there are two threads inside the synchronized block!). The bottom line: Code snippet - 1 & Code snippet - 2 are basically the same and thus are not thread safe...

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class Racer implements Runnable {
    public static AtomicInteger counter = new AtomicInteger(0);
    public static Boolean won = false;    

    @Override
    public void run() {
        synchronized (won) {
            System.out.println(counter.incrementAndGet()); //should be always 1; otherwise race condition
            if (!won) {
                won = true;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(counter.decrementAndGet()); //should be always 0; otherwise race condition
        }
    }   

    public static void main(String[] args) {
        int numberOfThreads = 20;
        ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);

        for(int i = 0; i < numberOfThreads; i++) {
            executor.execute(new Racer());
        }

        executor.shutdown();
    }
}
Multithreader
  • 878
  • 6
  • 14