即使线程持有 ReentrantLock 对象,也会发生 IllegalMonitorStateException

问题描述

即使该锁由 Writer 线程持有,但在运行下述代码时仍会发生以下异常。

作家已进入临界区!信息:在痛苦中保持坚强 锁定! 线程“Writer”中的异常 java.lang.IllegalMonitorStateException at java.lang.Object.notifyAll(本机方法

public class Buffer {
    private StringBuilder text;
    private final int MAX_PERMITS;
    private reentrantlock writerLock = new reentrantlock();
    private Semaphore semaphore;

    public Buffer(int maxPermits) {
        text = new StringBuilder();
        MAX_PERMITS = maxPermits;
        semaphore = new Semaphore(MAX_PERMITS);
    }

    public void write(String message) throws InterruptedException {
        writerLock.lock();
        System.out.println(ANSI_BLUE + Thread.currentThread().getName() + " has entered the critical section! Message: " + message);
        text.append(message); text.append(". ");
        Thread.sleep(2000);

        if(writerLock.isHeldByCurrentThread()) {
            System.out.println(ANSI_BLUE+"Lock held!");
            writerLock.notifyAll();
        }
        writerLock.unlock();
    }
    public void read() throws InterruptedException{
        if(text.length()==0) return;
        writerLock.lock();
        semaphore.acquire();
        System.out.println(ANSI_GREEN+Thread.currentThread().getName()+" read the following message: "+text.toString());
        semaphore.release();
        writerLock.unlock();
    }
}

解决方法

显式锁(如 ReentrantLock)本质上是一种独立于隐式“同步”锁的锁。换句话说,锁定/解锁 ReentrantLock 是与同步/等待/通知方案不同的方案。所以你需要决定你想使用哪种方案:

  • 对于显式锁,您需要在锁上创建条件(请参阅 Lock.newCondition() 方法),然后在条件上使用 signal() 和 await()。
  • 使用隐式锁,在调用wait()/notify()之前,您需要在有问题的对象上进行同步以获取锁

您的代码的问题本质上在于您试图在两种方案之间“混合和匹配”。

显式条件的优点是您可以创建不同的条件,因此在您的代码中明确说明您正在发出信号/等待的实际原因(条件)是什么。您还可以决定锁定是否“公平”。

隐式同步锁的缺点包括:

  • 每个对象只有一个,因此在您的代码中可能不太清楚为什么您实际上在等待/通知;
  • 同步锁本质上是“先进先出”,因此占用锁的线程可能会使其他线程在获得锁时长时间处于饥饿状态。