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

IllegalMonitorStateException occurs even when the thread holds the ReentrantLock object

即使Writer线程持有锁,当下面代码为运行时会出现以下异常。

作者进入临界区!信息:在你的痛苦中保持坚强 锁定! java.lang.Object.notifyAll(本机方法)

线程“Writer”java.lang.IllegalMonitorStateException 中的异常
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 等显式锁本质上是对隐式 'synchronised' 锁的一种独立类型的锁定。换句话说,locking/unlocking ReentrantLock 是 synchronized/wait/notify 方案的独立方案。所以你需要决定你想使用哪种方案:

  • 使用显式锁,需要在锁上创建条件(参见Lock.newCondition()方法),然后在条件上使用signal()和await()。
  • 使用隐式锁,在调用 wait()/notify() 之前,您需要在相关对象上同步以获得锁

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

显式条件的优点是您可以创建不同的条件,因此可以在您的代码中明确说明您 signalling/waiting 的实际原因(条件)是什么。您还可以决定是否锁定'fair'。

隐式同步锁的缺点包括:

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