views:

90

answers:

6

Right now i am trying to learn more about java threading, and i have a small question that i cannot find a direct answer to anywhere. Lets say i have two threadsthat both share an object:

public class FooA implements Runnable
{
    Object data;

    public FooA(final Object newData)
    {
        data = newData;
    }

    public void doSomething()
    {
        synchronized(data)
        {
            data = new Integer(1);
        }
    }

    public void run() {
        // Does stuff
    }
}

public class FooB implements Runnable
{
    Object data;

    public FooB(final Object newData)
    {
        data = newData;
    }

    public void doSomething()
    {
        synchronized(data)
        {
            System.out.println(data);
        }
    }
}

Would FooA block FooB when it is in the doSomething section of the code? Or vice versa? My gut feeling says yes, but according to the book i am reading it says no. Hence the need for monitor objects. I made a slightly more complex version of this, and everything worked fine.

I looked around a bit, but couldn't find a concrete answer.

+2  A: 

The problem is that one of the synchronised blocks assigns a new object to data. If that block starts first, and changes data, subsequent runs will be using a different object to lock on. So from then on, both will be able to run simultaneously.

Daniel Earwicker
If the data was mutable, would they then block each and every time?
UberJumper
If FooA is pointing to a different object, by assigning a new value to it's data item, FooB will never know, nor need to know. This is not a problem as such.
djna
@uberjumper - do you mean if data was *immutable*? If `data` was not having a new object assigned to it (i.e. the `data` field itself is never mutated) and both objects are constructed with the same object to be assigned to their `data` fields, then yes, both would properly block each other whenever they synchronised on `data`.
Daniel Earwicker
I did mean immutable :) I think i need my coffee.
UberJumper
@richj I think you are making this too complicated for Java. Java will pass the Object reference by value and once the reference inside changes in FooA, FooB will still be pointing at the same exact data it was before. It won't change because FooA changed it. I think this is what djna was getting at. This is why also why once FooA runs and changes it they run simultaneously from then on as Daniel pointed out. You do make a interesting point for pass by reference though...
Derek Litz
A: 

It's correct as far as you have to synchronize on the same object, but since one thread modifies the object referenced by "data", you would need to synchronize on "this".

richj
Only one of the threads appears to modify `data` from the initial shared value.
Daniel Earwicker
+1  A: 

The answer is yes in this case but it is brittle (i.e. the next change to the code will probably break something). Why does it work?

Because FooB never notices that FooA changes the object (each thread gets its own reference, so FooB never notices when FooA assigns its reference a new value).

For cases like that, I suggest to use AtomicReference which makes sure that two threads can access the same object and anyone can update that reference any time and the other threads get the new value only after the update.

Aaron Digulla
+1  A: 

Any Java Object may be used for synchronization.

If FooA and FooB are both constructed with references to same object then they are sharing the same "lock" object and will block as you are expecting. As the

  Object data;

decalrations are not final, either FooA or FooB, or both could assign different values to data, and then be synchronizing on different objects - which may be good or bad depending upon what you are trying to do.

djna
+1  A: 

In your code sample, fooA.data and fooB.data are not the same object unless someone initializes them as such with something like:

Object o = new Object();
FooA fooA = new FooA(o);
FooB fooB = new FooB(o);

Unless they are initialized to the same instance, they are not the same object, only the same type and name.

When FooA assigns new Integer(1) to data, they will again not be the same object, only the same type and name. So after that, they won't be synchronized unless you call:

fooB.data = fooA.data;

This would have to happen inside the synchronization block to guarantee synchronized execution.

Also,something to know about threading is that even if everything works once, that doesn't mean your program is correct or that it will work every time. Threading problems only occur when the timing happens to work out just right (or just wrong, as it were).

kerkeslager
+3  A: 

There are a few issues with this example.

Firstly, synchronized(data) means that it synchronizes on the object that is in data at the time. If you've initialized your two objects with the same object, you should get the synchronization.

However, since you're setting data itself within the code, this won't really work after that (since it won't be the same object then).

final in the constructor's parameter isn't particularly useful. It would be more useful as a modifier on the field itself. It wouldn't work in this particular example because you're modifying the value, but in general, it's a good way of preventing some concurrency issues when you know the value is going to be fixed.

I made a slightly more complex version of this, and everything worked fine.

It's very hard, or almost impossible, to debug concurrency issues by trial and error. The fact that it doesn't fail doesn't mean it will work reliably.

I'd recommend reading this book: http://www.javaconcurrencyinpractice.com/

Bruno
+1 for “It's very hard, or almost impossible, to debug concurrency issues by trial and error.” Except it's worse than that; even normal fuzzing techniques won't necessarily pick up concurrency problems. (Rolling it out into production and doing a high-profile demo though…)
Donal Fellows