简体   繁体   中英

Java: swing worker thread synchronization

In my application, I have one text field and a button. After focus lost from text field first swing worker (lets assume it as sw1) is called. Which opens a pop-up to populate value to put in text field. Second swing worker (lets assume it as sw2) is called after user clicks a button. Now the issue is that if I write something in text field and then click on button, sw1 is started first to calculate the value to put in text field and at the same time sw2 is also started. And sw2 finishes first and then sw1 populates result. What I want is sw2 should wait for sw1 to finish. Once sw1 finishes its task, it will notify sw2. I referred so many references over the internet and stackoverflow. This is the one which almost matches to my requirement. I tried to create a static final object inside class which starts sw1:

public final static Object lockObject = new Object();

Then inside done() method of sw1, I have written code like:

synchronized(lockObject) {
        sw1.notifyAll();
}

Inside doInBackground() method, of the second class, on first line, I have written code like:

synchronized(FirstClass.lockObject) {
    try {
        sw2.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

But I am getting java.lang.IllegalMonitorStateException, at java.lang.Object.notifyAll(Native Method). Can anybody tell me what is the issue and how to make it work the way I want.

Update: As per Ernest's solution I modified my code and it looks like now:

    FirstClass.java

    public final static Object lockObject = new Object();

    public static boolean flag = false;

    someMethod() {
        synchronized(lockObject){
            sw1.doInbackground() {
            ......
            }

            sw1.done() {
            .....
            flag = true;
            lockObject.notifyAll();
            }

        }
    }


    SecondClass.java

    anotherMethod() {
        sw2.doInbackground() {
            try {
                while (!FirstClass.flag) {
                    FirstClass.lockObject.wait();
                }
                FirstClass.flag = false;
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            ......
        }

    }

But still I am getting java.lang.IllegalMonitorStateException on lockObject.notifyAll() line. Can you please tell if I am doing it correctly?

Thanks.

Your code should look something like this.

FirstClass.java

public final static Object lockObject = new Object();

public static boolean flag = false;

someMethod() {
    sw1.doInbackground() {
    ......
    }

    sw1.done() {
    .....
    }

    synchronized(lockObject){
        flag = true;
        lockObject.notifyAll();
    }
}


SecondClass.java

anotherMethod() {
    sw2.doInbackground() {
        try {
            synchronized(lockObject){
                while (!FirstClass.flag) {
                    FirstClass.lockObject.wait();
                }
                FirstClass.flag = false;
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ......
    }

}

But. Synchronizing on a global static object will get you in trouble if you have more than one instance of FirstClass.java and SecondClass.java. You should really find a way to pass the object instances around.

If I understood correctly your use case, can't you simply disable the button for sw2 when the user starts editing the field, and re-enable it when the first worker finishes? It would be much more clear for the user as well.

You need not to reinvent such a simple synchronization facility. For example, you can use CountDownLatch . Sw1 does countdown and sw2 - await.

You can only call wait() and notify() on an object whose monitor you hold. In each of your code snippets, you're locking one object, but calling these methods on another. It just doesn't work that way. I'm afraid I can't quite make out what you're trying to do, so it's hard to give you specific corrections, but basically, these blocks need to look something like

synchronized(sw2) {
    try {
        sw2.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Let's say there are two threads, T1 and T2, and there is some object O1. Then if code running on thread T1 wants to wait until code in thread T2 says it's OK to continue, it must synchronize on object O1 and then call O1.wait() . When code running on T2 wants to send that message to T1, it must synchronize on O1 and call O1.notify() (or O1.notifyAll() .) It doesn't matter what object you use for O1 , but the code in both threads must agree to use the same object.

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