简体   繁体   中英

how can I change the lock I have aquired in a synchronized block, change it, and notifyAll() without getting java.lang.IllegalMonitorStateException?

I know that questions have been asked about changing the lock in a synchronized block, but in my client side of multiThreaded socket programming I have a status enumeration in my class and every time I want to change it I get the lock and make the changes. At the same time another thread is waiting (it also gets the same lock and after reaching the wait method lets go of it) to observe the changes after the changer thread makes its changes and calls notifyAll(). now if I want to notifyAll() on an object(i mean the state enum) which is changed I get java.lang.IllegalMonitorStateException! I considered aquiring another shared final object as my lock but in this way maybe the threads still be able to manipulate state enum. what is the best approach?? Any answer will be appreciated. this is part of my code:

this is the changer thread:

synchronized (client.getStatus()) {
    client.setStatus(ClientState.successfulRegistration);
    client.getStatus().notifyAll()
}

and this is the waiting thread:

synchronized (client.getStatus()){
    try {
            client.getStatus().wait();
            System.out.println("signMeIn notified");
            ClientState result = client.getStatus();
            System.out.println(result);
            if(result.toString().equalsIgnoreCase("successfulSignIn") )
                //do sth
        } catch (InterruptedException e) {
            e.printStackTrace();
    }

There is an error in this code:

synchronized (client.getStatus()) {
    client.setStatus(ClientState.successfulRegistration);
    client.getStatus().notifyAll()
}

You are only allowed to notifyAll() on an object you locked. But one line above you replaced the object with a different one. It is a really bad idea to change a lock object. Usually you will use "this" or you use a private object.

I would suggest the following solution:

public synchronized void setStatus(T status) {}
public synchronized T getStatus() {}

The wait() and notifyAll() methods are only needed if you are waiting on a condition and want to release the lock. Here an example:

public synchronized void add(T element) {
   while(full) {
     wait();
   }
   data.add(element);
   notifyAll();
}

public synchronized T remove() {
    while(empty) {
      wait();
    }
    T item = data.getAndremove();
    notifyAll();
    return item;
}

In this examples are many hidden concepts. First you need a while-loop for the waiting. Because if you wait and gets notified you are back in the ready-to-run-queue. But you do not know if the condition still holds so that you have to recheck the condition. This is why you use a while-loop instead of a if().

The second concept is known by notify-and-resume. It is possible to notify() and keep the lock. So you do not have to put the notify() at the end.

The third concept is the difference between notify() and notifyAll(). The first one only wakes one thread. If the data is empty and you wakes up a thread that wants to remove something so there would be a dead-lock. If you wake-up all, all will check if the condition is met and try to proceed.

Here an other example that matches more your code:

public synchronized void doSth() {
   while(client.getStatus.equals("WAIT") {
        wait();
   }
   System.out.println("Status isn't wait");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM