web-dev-qa-db-fra.com

java.lang.IllegalMonitorStateException: objet non verrouillé par un thread avant wait ()?

J'utilise progress dialog.i dois arrêter le fil de discussion lorsque l'utilisateur ferme le progressdialog. Malheureusement, il doit m'aider à obtenir une exception.

En classe intérieure

class UpdateThread extends Thread{

    public  void run() {
        while (true){
            count=adapter.getCount();

            try {
               mHandler.post(  new Runnable() {
                    public  void run() {
                        Log.i(TAG,count+"count");
                        progressDialog.setMessage(count + "Device  found");
                    }
                });
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Oncreate

 updateThread=new UpdateThread();

 progressDialog= new ProgressDialog(GroupListActivity.this);
 synchronized (this) {
     updateThread.start();
 }

ondismissal

   progressDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
        @Override
        public  void onDismiss(DialogInterface dialog) {
            try {
                synchronized (this) {
                    updateThread.wait(300);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG,"Thread is stopped");
        }
    });
40
Asthme

C'est faux:

synchronized(foo) {
    foo.wait();
}

Le problème est, qu'est-ce qui va réveiller ce fil? C'est-à-dire, comment pouvez-vous garantir que l'autre thread n'appelle pas foo.notify()avant le premier thread appelle foo.wait() ? C'est important car l'objet foo ne se souviendra pas qu'il a été averti si l'appel de notification a lieu en premier. S'il n'y a qu'un seul notify (), et si cela se produit avant wait (), alors wait () ne retournera jamais.

Voici comment attendre et notifier étaient censés être utilisés:

private Queue<Product> q = ...;
private Object lock = new Object();

void produceSomething(...) {
    Product p = reallyProduceSomething();
    synchronized(lock) {
        q.add(p);
        lock.notify();
    }
}

void consumeSomething(...) {
    Product p = null;
    synchronized(lock) {
        while (q.peek() == null) {
            lock.wait();
        }
        p = q.remove();
    }
    reallyConsume(p);
}

Les éléments les plus importants à noter dans cet exemple sont qu’il existe un test explicite pour la condition (par exemple, q.peek ()! = Null) et que personne ne modifie la condition sans verrouiller le verrou.

Si le consommateur est appelé en premier, il trouvera la file vide et attendra. Il n'y a pas de moment où le producteur peut se glisser dans, ajouter un produit à la file d'attente, puis notifier le verrouillage jusqu'à ce que le consommateur soit prêt à recevoir cette notification.

Par ailleurs, si le producteur est appelé en premier, il est garanti au consommateur de ne pas appeler wait ().

La boucle du consommateur est importante pour deux raisons: la première est que, s'il existe plusieurs threads de consommation, il est possible qu'un consommateur reçoive une notification, puis qu'un autre se faufile et vole le produit dans la file d'attente. Dans ce cas, la seule chose raisonnable que le premier consommateur puisse faire est d’attendre le prochain produit. L'autre raison pour laquelle la boucle est importante est que Javadoc indique que Object.wait () est autorisé à retourner même lorsque l'objet n'a pas été notifié. C'est ce qu'on appelle un "réveil parasite", et la bonne façon de le gérer est de revenir en arrière et d'attendre à nouveau.

Remarque: le verrou est private et la file d'attente est private. Cela garantit qu'aucune autre unité de compilation ne va interférer avec la synchronisation dans cette unité de compilation.

Et remarque: le verrou est un objet différent de la file d'attente elle-même. Cela garantit que la synchronisation dans cette unité de compilation n'interférera pas avec la synchronisation que l'implémentation de Queue implémente (le cas échéant).


NOTE: Mon exemple réinvente une roue pour prouver un point. En code réel, vous utiliseriez les méthodes put () et take () d'un ArrayBlockingQueue qui prendrait en charge toutes les attentes et les notifications pour vous.

57
Solomon Slow

Vous ne pouvez attendre qu'un objet si vous le verrouillez déjà, vous pouvez essayer:

synchronized (updateThread) {
    updateThread.wait(300);
}

... mais je ne suis pas vraiment sûr de ce que vous essayez de réaliser avec les serrures.

9
BarrySW19

On dirait vraiment que vous essayez d'utiliser synchronized et wait là où vous ne devriez pas être.

Si vous voulez vraiment attendre que le fil se termine, vous devriez faire quelque chose comme ça

Dans votre UpdateThread:

class UpdateThread extends Thread{
    public AtomicBoolean stopped = new AtomicBoolean(false);
    public  void run() {
        while (!stopped.get()){
    .....

Dans ta création:

updateThread = new UpdateThread();
progressDialog = new ProgressDialog(GroupListActivity.this);
updateThread.start();    // no synchronization necessary

Dans votre congédiement:

progressDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
        @Override
        public  void onDismiss(DialogInterface dialog) {
            try {
                updateThread.stopped.set(true);
                updateThread.join(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.i(TAG,"Thread is stopped");
        }
    });

Remarque, j'ai ajouté une condition de sortie à votre thread afin qu'il s'arrête réellement (tel quel, votre thread continue à fonctionner). Vous voudrez probablement rendre la condition de sortie privée et ajouter un passeur pour la propreté. De plus, j'utilise join pour attendre que votre thread se termine.

0
Krease