Android java.lang.IllegalMonitorStateException: ob

2019-01-26 08:29发布

问题:

I define a global static object as a synchronization lock.

public static Object ConfirmationSynObj = new Object();

The following function is what I wrote, but it throw a IllegalMonitorStateException.

       synchronized (Config.ConfirmationSynObj) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    //this is a http request
                    appSignInfo = getAPKSignature(context, pkinfo.packageName);
                    Config.ConfirmationSynObj.notify();
                }
            }).start();
            try {
                Config.ConfirmationSynObj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (appSignInfo == null) {
                return ret;
            }
        }

Does anyone know how to lock an object or a function in order to prevent the concurrency?

回答1:

A common replacement for wait/notify is CountDownLatch. (From java.util.concurrent as well but working kind of inverse of Semaphore - see answer by Tom)

You initialize it to the amount of steps required, threads that have finished count down and some other place waits for the countdown to reach 0.

void doFoo() {
    final CountDownLatch latch = new CountDownLatch(1);
    new Thread(new Runnable() {

        @Override
        public void run() {
            //this is a http request
            appSignInfo = getAPKSignature(context, pkinfo.packageName);
            latch.countDown();
        }
    }).start();
    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    if (appSignInfo == null) {
        return ret;
    }
}

But the code you wrote there can be simplified to

void doFoo() {
    return getAPKSignature(context, pkinfo.packageName);
}

You start a second thread to do something and all you do in that time is to wait. If there is nothing to do while that task is running don't create an extra thread. The result is the same.

If you try to do a HTTP request outside of the UI thread because you get that NetworkOnMainThreadExcpeption, you have to do it differently. While Android won't detect your code as long time blocking code it still is. Use an AsyncTask for example.



回答2:

@Kayaman speaks correctly, as far as I can tell, however if I may humbly suggest: java.util.concurrent can save you lots of time!

What I'd use there is a semaphore.

From the docs: "Each acquire() blocks if necessary until a permit is available, and then takes it.".

But there are other choices too- I strongly recommend using this where possible, as you should avoid lots of pit falls as in your case.

        Semaphore semaphore = new Semaphore(0);
        new Thread(new Runnable() {

            @Override
            public void run() {
                //this is a http request
                appSignInfo = getAPKSignature(context, pkinfo.packageName);
                semaphore.release();
            }
        }).start();
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


回答3:

You might be creating and starting the thread in the synchronized block, but when the thread comes to Config.ConfirmationSynObj.notify(); you'll notice that there's no synchronization.

You'll need to add a synchronized block inside run().



回答4:

 new Thread(new Runnable() {

            @Override
            public void run() {

Above thread is not owning lock on ConfirmationSynObj object hence throwing IllegalMonitorStateException

Use one more synchronized block inside run method

           @Override
            public void run() {
            synchronized (Config.ConfirmationSynObj) {
                //this is a http request
                appSignInfo = getAPKSignature(context, pkinfo.packageName);
                Config.ConfirmationSynObj.notify();
               }
            }