0

How do I obtain multiple locks in cases like this:

public class DoubleCounter
{
    private int valA = 0;
    private int valB = 0;

    private Object lockA = new Object();
    private Object lockB = new Object();

    public void incrementA()
    {
        synchronized (lockA)
        {
            valA++;
        }
    }

    public void incrementB()
    {
        synchronized (lockB)
        {
            valB++;
        }
    }

    public void print()
    {
        //I have to obtain both lockA & lockB before executing this:
        System.out.format("valA: %d\nvalB: %d", valA, valB);
    }
}

Keep in mind I don't want the execution of incrementA() by one thread to block the execution of incrementB() by another - therefore I don't want to use synchronized methods.

Similar (not exactly the same) question has been asked twice on StackOverflow but I still haven't got the answer I was looking for. The only thing I know is that I shouldn't nest one synchronized() inside another synchornized().

matt-pielat
  • 1,659
  • 3
  • 20
  • 33
  • 1
    Yes, you *should* nest synchronized blocks. That's the only way. There is no primitive which would make your thread wait until it can take both locks and then take them both at once. Enforce a lock acquisition order to prevent deadlocks. – Marko Topolnik Jul 13 '14 at 12:34
  • 1
    Just make sure you don't do `lockA -> lockB -> stuff` somewhere and then `lockB -> lockA -> stuff` as you will get a deadlock. Incidentally, you should use the new [`Lock`](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html) API rather than `Object` - it makes things more explicit. – Boris the Spider Jul 13 '14 at 12:51
  • Take a look at [this](http://stackoverflow.com/questions/16382193/is-it-safe-to-lock-multiple-reentrantreadwritelocks-in-the-same-try-block). – Boris the Spider Jul 15 '14 at 14:00

1 Answers1

0

Consider an option of

public void print()
{
    final int localA;
    synchronized(lockA)
    {
        localA = valA;
    }

    final int localB;
    synchronized(lockB)
    {
        localB = valB;
    }

    //I have to obtain both lockA & lockB before executing this:
    System.out.format("valA: %d\nvalB: %d", localA, localB);
}

Another option would be to simply use an AtomicInteger, which is much simpler.

Brett Okken
  • 6,210
  • 1
  • 19
  • 25
  • This somewhat solves this specific problem, but what if it's not integers I'm working with and making deep copies of shared objects is memory-costly? – matt-pielat Jul 13 '14 at 13:08
  • 1
    Then you will have to nest your locks and be /very/ careful that nested locks are /always/ obtained in the same order. – Brett Okken Jul 13 '14 at 13:19
  • NO! This means that the state will not be consistent. Something could happen to `valB` before you read it, but after reading `valA`. This is completely useless if the OP wants to print consistent state (which given the question is the case). – Boris the Spider Jul 13 '14 at 14:47
  • @BoristheSpider It is far from clear the OP is overly concerned about an inconsistent state (between 2 apparently uncoordinated objects). The clearly stated desire was to avoid nested synchronization. It appears from the response, however, that the question posed is artificially simplified from the real use case. – Brett Okken Jul 13 '14 at 22:06
  • I don't agree - the comment in the code snippet in the question clearly states that the OP wants to acquire both locks before printing. But the OP wanted a way to acquire them simultaneously rather than sequentially. – Boris the Spider Jul 13 '14 at 22:09
  • @lavsprat Is the intended use case printing out the data? Because instead of synchronizing to get a deep copy of the object, you could get a string representation of each object with just the single lock held and then concat the strings outside of any synchronization block. – Brett Okken Jul 13 '14 at 22:09
  • @BrettOkken No, this is just a simple example. I'd like to know what's the proper course of action in general case (not exactly printing). – matt-pielat Jul 15 '14 at 13:26
  • @BoristheSpider Yes, simultaneously. – matt-pielat Jul 15 '14 at 13:28