Search code examples
illegalstateexception

IllegalMonitorStateException occurs even when the thread holds the ReentrantLock object


Even though the lock is held by the Writer thread, the following exception occurs when the below-mentioned code is run.

Writer has entered the critical section! Message: stay strong through your pain Lock held! Exception in thread "Writer" java.lang.IllegalMonitorStateException at java.lang.Object.notifyAll(Native Method)

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();
    }
}

Solution

  • Explicit locks such as ReentrantLock are essentially a separate type of locking to the implicit 'synchronised' locks. In other words, locking/unlocking a ReentrantLock is a separate scheme to the synchronized/wait/notify scheme. So you need to decide which scheme you want to use:

    • With an explicit lock, you need to create a Condition on the lock (see the Lock.newCondition() method), then use signal() and await() on the condition.
    • With an implicit lock, before calling wait()/notify(), you need to synchronise on the object in question to acquire the lock

    The problem with your code is essentially that you're trying to "mix and match" between the two schemes.

    The advantage of the explicit Condition is that you can create different Conditions and therefore make it clear in your code what the actual reason (condition) is that you are signalling/waiting for. You can also decide whether the lock is 'fair' or not.

    Disadvantages of an implicit synchronized lock include:

    • there's only one of them per object, so it can be less clear in your code why you are actually waiting/notifying;
    • synchronized locks are essentially "first in, first out", so threads that hog the lock can starve other threads for long periods of time from obtaining it.