0

According to the documentation for ReentrantLock.newCondition(), the calling thread needs to own a lock before calling a signaling method:

If this lock is not held when any of the Condition waiting or signalling methods are called, then an IllegalMonitorStateException is thrown.

Indeed, this is what I see when I try it:

java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signalAll(AbstractQueuedSynchronizer.java:1954)
    at PingPongPrinter_.printPing(PingPong2.java:35)
    at WorkerPing.run(PingPong2.java:53)
    at java.lang.Thread.run(Thread.java:748)

So why do this restriction exists in java.util.concurrent.locks.ReentrantLock? I believe there is no such restriction in C++.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Suvam Roy
  • 963
  • 1
  • 8
  • 19
  • It says in the [documentation](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html#newCondition()) *"If this lock is not held when any of the Condition waiting or signalling methods are called, then an IllegalMonitorStateException is thrown."* – markspace Feb 01 '23 at 14:04
  • "Since I have made proper lock on the shared resource why still its throwing the exception ?" because you need to own the lock in order to call signal(), or signalAll() (per documentation). "How come there is a clash as I am signaling only after changing the shared resource and printing sysout statement?" same reason. "Why do I need to put signalAll inside lock? " now, that's an intersesting question. May I suggest you rephrase your question something like "The documentation says I need to own the lock, but why does it puts this restriction?" – Étienne Miret Feb 01 '23 at 14:09
  • @ÉtienneMiret Yes please rephrase I would like to know why so? – Suvam Roy Feb 01 '23 at 14:13
  • @SuvamRoy I edited your question. Feel free to revert my edit if the new phrasing doesn't suits you. – Étienne Miret Feb 01 '23 at 14:19
  • 2
    If you're not holding the lock when notifying, there is no guarantee of the visibility of changes to the recipient. Requiring you to hold the lock when notifying ensures correctness, removing or reducing the chance of buggy code (e.g. code notifying without ever holding the lock). – Mark Rotteveel Feb 01 '23 at 14:23
  • @MarkRotteveel Thanks. Your comment clears my doubt. If you don't mind please give a full answer so that I can accept it. – Suvam Roy Feb 01 '23 at 14:59
  • 1
    Re, "why do this restriction exists...?" It's for the same reason that `object.wait()` and `object.notify()` may only be used inside a `synchronized(object)` block. It's meant to help you to avoid making a bad mistake, which you can read about here: https://stackoverflow.com/a/26593239/801894 – Solomon Slow Feb 01 '23 at 17:18

1 Answers1

1

Answer to original question

You're unlocking a lock associated with the condition before signaling it. From documentation:

An implementation may (and typically does) require that the current thread hold the lock associated with this Condition when this method is called. Implementations must document this precondition and any actions taken if the lock is not held. Typically, an exception such as IllegalMonitorStateException will be thrown.

Also, only lock for the time required to modify the value you are conditioning for.

void printPong() throws Exception {

  // wait for pong condition
  while (isPing) {
    blockPong.await();
  }

  rlock.lock();
  isPing = true; // modify value
  blockPing.signalAll(); // signal ping
  rlock.unlock();
  
  System.out.println("Pong");
}

void printPing() throws Exception {

  // wait for ping condition
  while (!isPing) {
    blockPing.await();
  }

  rlock.lock();
  isPing = false; // modify value
  blockPong.signalAll(); // signal pong
  rlock.unlock();

  System.out.println("Ping");

}

Answer to modified question

So why do this restriction exists in java.util.concurrent.locks.ReentrantLock? I believe there is no such restriction in C++.

Because ReentrantLock is mutually exclusive. It provides access to one single thread at any given time. That's a design choice.

In C++ the std::condition_variable also requires you to own a mutex to the resource in order to signal.

rgnt
  • 551
  • 2
  • 7