javamultithreadingsemaphoreillegalmonitorstateexcep

That IllegalMonitorStateException...thread not owning the object? Can't be, there's only one


I have this class, which is a basic approach to a file locking utility (doesn't interact with the locks from the OS tho). The idea is to have a static HashMap which stores pair of references to File objects that are used by the application with binary semaphores. Upon first access to a new file the pair is stored into the variable. The problem is that the .wait() line throws IllegalMonitorStateException, and I can't understand why, because I've created a project to test this class with only one thread, so it's impossible that the thread accessing the method doesn't own the object, isn't it?

public abstract class FileLocker {

    private static final HashMap<File, Semaphore> locksMap = new HashMap<>();

    public synchronized static final void getLock(final File file) {
        if (!FileLocker.locksMap.containsKey(file)) {
            FileLocker.locksMap.put(file, new Semaphore(1, Boolean.TRUE));
        }
        try {
            FileLocker.locksMap.get(file).wait();
        } catch (final InterruptedException e) {
            SysLogger.log(e, "ERR0", SysLogger.Level.CRASH);
        }
        if (file.isDirectory()) {
            for (final File f : file.listFiles()) {
                if (f.isDirectory()) {
                    FileLocker.getLock(f);
                }
            }
        }
    }

    public synchronized static final void releaseLock(final File file) {
        if (file.isDirectory()) {
            for (final File f : file.listFiles()) {
                if (f.isDirectory()) {
                FileLocker.releaseLock(f);
                } else {
                FileLocker.locksMap.get(file).notify();
                }
            }
        }
        FileLocker.locksMap.get(file).notify();
    }
}

My intention was that the methods weren't synchronized, but since I started receiving this exception, I changed them to synchronized so they theoretically make sure that the thread entering them owns the resources used, but it's not working, the same exception arises.

Exception trace: Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:503)
at org.lsp.io.files.FileLocker.getLock(FileLocker.java:18)
at org.lsp.main.Main.main(Main.java:9)

Called with

FileLocker.getLock(Paths.get("default.xml").toFile());

Solution

  • FileLocker.locksMap.get(file).wait();

    should be

    FileLocker.locksMap.get(file).acquire();

    wait in this case is waiting on the Object monitor. Since you are not synchronized on the semaphore ie:

    Semaphore s = FileLocker.locksMap.get(file);
    synchronized(s){
         s.wait();
    }
    

    you get your exception. The acquire is offered by Semaphore and handles that synchronization for you.

    Note You'll also run into the same situation with notify() instead of release()