无法理解此多线程异常行为

Can't understand this multi-threading exception behavior

提问人:Suvam Roy 提问时间:2/1/2023 最后编辑:Mark RotteveelSuvam Roy 更新时间:2/1/2023 访问量:66

问:

根据 ReentrantLock.newCondition() 的文档,调用线程在调用信令方法之前需要拥有一个锁:

如果在调用任何 Condition 等待或信号方法时未保持此锁,则会引发 IllegalMonitorStateException。

事实上,这是我尝试时看到的:

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)

那么为什么这个限制存在于 ?我相信C++中没有这样的限制java.util.concurrent.locks.ReentrantLock

Java 多线程 异常

评论

0赞 markspace 2/1/2023
它在文档中说:“如果在调用任何 Condition 等待或信号方法时未持有此锁,则会引发 IllegalMonitorStateException。
0赞 Étienne Miret 2/1/2023
“既然我已经对共享资源进行了适当的锁定,为什么它仍然抛出异常?”因为您需要拥有锁才能调用 signal() 或 signalAll()(根据文档)。“为什么会有冲突,因为我只是在更改共享资源并打印 sysout 语句后才发出信号?” 同样的原因。“为什么我需要把 signalAll 放在锁里?”现在,这是一个令人费解的问题。我是否可以建议你重新表述你的问题,比如“文档说我需要拥有锁,但为什么它设置了这个限制?
0赞 Suvam Roy 2/1/2023
@ÉtienneMiret 是的,请改写一下,我想知道为什么会这样?
2赞 Mark Rotteveel 2/1/2023
如果您在通知时没有按住锁,则无法保证对收件人可见更改。要求您在通知时按住锁可确保正确性,消除或减少错误代码的机会(例如,在不按住锁的情况下通知代码)。
1赞 Solomon Slow 2/2/2023
回复,“为什么存在此限制......?出于同样的原因,并且只能在块内使用。它旨在帮助您避免犯一个糟糕的错误,您可以在此处阅读:stackoverflow.com/a/26593239/801894object.wait()object.notify()synchronized(object)

答:

1赞 rgnt 2/1/2023 #1

对原始问题的回答

在发出信号之前,您正在解锁与条件关联的锁。从文档:

在调用此方法时,实现可能(并且通常确实)要求当前线程保留与此 Condition 关联的锁。实现必须记录此前提条件以及未保持锁定时采取的任何操作。通常,将引发异常,例如 IllegalMonitorStateException。

此外,仅锁定修改要调节的值所需的时间。

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");

}

对修改问题的回答

那么,为什么java.util.concurrent.locks.ReentrantLock中存在此限制呢?我相信C++中没有这样的限制。

因为是相互排斥的。它提供在任何给定时间对单个线程的访问。这是一个设计选择。ReentrantLock

在 C++ 中,还要求您拥有资源的互斥锁才能发出信号。std::condition_variable

评论

0赞 Suvam Roy 2/1/2023
您能简化一下文档吗?我不太明白