Android java.lang.IllegalMonitorStateException: 在wait()方法之前线程未锁定对象

11

我将一个全局静态对象定义为同步锁。

public static Object ConfirmationSynObj = new Object();
我写的下面这个函数会抛出 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;
            }
        }

有没有人知道如何锁定对象或函数以防止并发?


5
请遵循Java的命名规范。 - Sotirios Delimanolis
4个回答

10
一个常用的替代 wait/notify 的方法是 CountDownLatch。CountDownLatch 也来自 java.util.concurrent,但与 Semaphore 工作方式相反。您可以参考 Tom 的回答。您需要将 CountDownLatch 初始化为所需步骤的数量,当线程完成时进行倒计时,其他一些地方等待倒计时达到 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;
    }
}

但是你写的那段代码可以简化为:

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

如果您开启第二个线程来执行某些操作,但在此期间所有您所做的就是等待,则如果该任务正在运行时没有其他事情可做,请不要创建额外的线程。结果是一样的。

如果您尝试在UI线程之外进行HTTP请求,因为您得到了NetworkOnMainThreadExcpeption异常,您必须以不同的方式处理它。虽然Android不会将您的代码检测为长时间阻塞代码,但实际上它仍然是。例如,使用AsyncTask。


@user924 你的评论有什么意义?是的,它们是不同的,但锁存器将满足wait/notify的一部分情况。 - zapl

5

据我所知,@Kayaman说得很正确,但是我谨卑地建议:使用java.util.concurrent可以节省很多时间!

在这种情况下,我会使用信号量(Semaphore)

从文档中可以看到:“每个acquire()方法如果需要会阻塞直到许可证可用,然后获取它。”。

但也有其他选择-我强烈建议在可能的情况下使用此方法,因为您应该避免像您的情况一样遇到很多问题。

        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
 new Thread(new Runnable() {

            @Override
            public void run() {

以上线程没有锁定ConfirmationSynObj对象,因此抛出IllegalMonitorStateException

run方法中再使用一个同步块

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

2

你可能会在同步块中创建和启动线程,但当线程到达Config.ConfirmationSynObj.notify();时,你会发现没有同步。

你需要在run()内部添加一个同步块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接