web-dev-qa-db-ja.com

Android Java.lang.IllegalMonitorStateException:オブジェクトはwait()の前にスレッドによってロックされていません

グローバル静的オブジェクトを同期ロックとして定義します。

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;
            }
        }

並行性を防ぐためにオブジェクトまたは関数をロックする方法を知っている人はいますか?

10
Folee

wait/notifyの一般的な置換はCountDownLatchです。 (Java.util.concurrentからも同様ですが、Semaphoreの逆の働きをします-トムの回答を参照してください)

必要なステップ数に初期化し、カウントダウンを終了したスレッドやその他の場所で、カウントダウンが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);
}

何かをするために2番目のスレッドを開始し、その間に行うのは待つことだけです。そのタスクの実行中に何もすることがない場合は、余分なスレッドを作成しないでください。結果は同じです。

NetworkOnMainThreadExcpeptionを取得したためにUIスレッドの外部でHTTPリクエストを実行しようとすると、別の方法で実行する必要があります。 Androidは、コードをブロックしている間はコードを検出しませんが、たとえばAsyncTaskを使用します。

8
zapl

@Kayamanは私が知る限り正しく話しますが、謙虚に提案することができれば:Java.util.concurrentあなたに多くの時間を節約することができます!

私が使用するのは セマフォ です。

ドキュメントから:「許可が利用可能になるまで、必要に応じて各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();
        }
5
Tom
 new Thread(new Runnable() {

            @Override
            public void run() {

上記のスレッドはConfirmationSynObjオブジェクトのロックを所有していないため、IllegalMonitorStateExceptionをスローします

runメソッド内でもう1つの同期ブロックを使用します

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

同期されたブロックでスレッドを作成して開始している可能性がありますが、スレッドがConfig.ConfirmationSynObj.notify();になると、同期が行われていないことに気付くでしょう。

Run()内に同期ブロックを追加する必要があります。

2
Kayaman