Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I getting IllegalMonitorStateException?

I get the following Exception being thrown when I try to unlock an object.

Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
    at java.util.concurrent.locks.ReentrantLock.unlock(Unknown Source)
    at Pipe.unlock(Pipe.java:21)
    at Station.doWork(Station.java:81)
    at Station.run(Station.java:66)
    at java.lang.Thread.run(Unknown Source)

All that Pipe.unlock is doing is the following:

public void unlock(){
   accessLock.unlock();
}

Where accessLock is an ReentrantLock

Do you know where the issue could be?

EDIT:

This is the run method in Station

if(Pipes[inConnection].accessLock.tryLock()){
    System.out.println("Station "+ StationNumber+": granted access to pipe "+inConnection+".");

//This is just a way for me to keep track if both pipes have been granted
            if(connected<0)
                connected=inConnection;
            else
                connected+=inConnection;
}


if(Pipes[outConnection].accessLock.tryLock()){
            System.out.println("Station "+ StationNumber+": granted access to pipe "+outConnection+".");

    //This is just a way for me to keep track if both pipes have been granted
    if(connected<0)
        connected=outConnection;
    else
        connected+=outConnection;   
}


        doWork();

While this is the doWork method:

private void doWork() {
    if(connected==inConnection+outConnection){
        System.out.println("Station "+StationNumber+": successfully flows "+inConnection+".");
        System.out.println("Station "+StationNumber+": successfully flows "+outConnection+".");

        Pipes[inConnection].unlock();
        System.out.println("Station "+StationNumber+": released access to pipe "+inConnection+".");

        Pipes[outConnection].unlock();
        System.out.println("Station "+StationNumber+": released access to pipe "+outConnection+".");

        try {
            Thread.sleep(rand.nextInt(200));
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        WorkLoad--;
    }else if(connected >=0 ){
        Pipes[connected].unlock();
        System.out.println("Station "+StationNumber+": released access to pipe "+connected);

    }

    connected=-1;
}
like image 761
Kelsey Abreu Avatar asked Jun 02 '13 15:06

Kelsey Abreu


2 Answers

I know this question is more than a year old, but I was facing the same problem and the solution turned out to be not another Thread that was holding the Lock somehow but basically a very simple mistake and the inner details of a ReentrantLock. If we look at the implementation of tryRelease:

protected final boolean tryRelease(int releases) {
  int c = getState() - releases;
  if (Thread.currentThread() != getExclusiveOwnerThread())
     throw new IllegalMonitorStateException();
  ..
  if (c == 0) {
    ..
    setExclusiveOwnerThread(null);
  }
  ..
}

If the release-count drops to zero, the exclusiveOwnerThread is set to null. And if you afterwards try to release the lock once more, you're not the exclusiveOwnerThread anymore, since your Thread is unlikely to be null. So one simple .unlock() too much can lead to this (in this situation rather confusing) Exception.

like image 75
xor_eq Avatar answered Oct 10 '22 18:10

xor_eq


The documentation is pretty clear:

If the current thread is the holder of this lock then the hold count is decremented. If the hold count is now zero then the lock is released. If the current thread is not the holder of this lock then IllegalMonitorStateException is thrown.

So the thread which is trying to unlock isn't the holder of the lock. We can't tell why you expected it to be the same thread without seeing more of your code.

like image 10
Jon Skeet Avatar answered Oct 10 '22 18:10

Jon Skeet