web-dev-qa-db-fra.com

Threading en Java: comment verrouiller un objet?

La fonction suivante s'exécute dans son propre thread:

private void doSendData()
{
    try {

           //writeToFile(); // just a temporary location of a call
           InetAddress serverAddr = InetAddress.getByName(serverAddress);
           serverAddr.wait(60000);
           //Log.d("TCP", "C: Connecting...");
           Socket socket = new Socket(serverAddr, portNumber);
           socket.setSoTimeout(3000);

               try {
                //Log.d("TCP", "C: Sending: '" + message + "'");
                PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())),true);
                String message = packData();
                out.println(message);
                Log.d("TCP", "C: Sent.");
                Log.d("TCP", "C: Done.");
                connectionAvailable = true;

             } catch(Exception e) {
                 Log.e("TCP", "S: Error", e);
                 connectionAvailable = false;

               } finally {
                  socket.close();
                  announceNetworkAvailability(connectionAvailable);
                }

         } catch (Exception e) {
              Log.e("TCP", "C: Error", e);
              announceNetworkAvailability(connectionAvailable);
         }
}

Lorsque l'exécution atteint la ligne serverAddr.wait(60000), une exception se déclenche:

Java.lang.IllegalMonitorStateException: object not locked by thread before wait()

Quelqu'un sait-il comment verrouiller un objet ou une fonction afin d'empêcher la concurrence? J'ai essayé d'ajouter un objet Verrouillé:

private final Lock lock = new ReentrantLock();

et la ligne

boolean locked = lock.tryLock();

au début de la fonction mais cela n'a pas fonctionné.

33
Niko Gamulin

Pour appeler wait () sur un objet, vous devez maintenir le verrou synchronisé sur cet objet (bien que le verrou soit réellement libéré pendant que le thread est en attente):

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

Je dois admettre que pourquoi vous voulez faire cela me déconcerte dans ce cas ...

64
Neil Coffey

Peut-être que la méthode que vous recherchez est Thread.sleep (long) ? Cette méthode wait (comme dans l'arrêt de l'exécution du thread) pendant le délai spécifié en millisecondes avant de reprendre. 

object.wait (long) (c'est ce que vous utilisez) fait quelque chose de complètement différent. Il attend qu'un autre objet d'un autre thread le notifie (c'est-à-dire: lui envoie une sorte de message de réveil) et attend au plus le nombre de millisecondes spécifié. Compte tenu du code que vous avez posté, je doute fort que ce soit ce que vous souhaitiez vraiment.

Si Thread.sleep () n’est pas ce que vous voulez, vous devez utiliser le bloc synchronisé comme indiqué par les autres afficheurs.

21
LordOfThePigs

Je grince toujours quand je vois ce genre de code. Faites-vous une faveur et jetez un coup d'œil au paquet Java.util.concurrent.

11
Apocalisp

Les réponses ci-dessus sont correctes. Vous pouvez utiliser un bloc de code synchronisé. Ou vous pouvez créer ce qu'ils appellent un mutex. Un mutex peut en réalité être n'importe quel objet. Beaucoup de gens utilisent simplement Object lui-même comme un mutex. Ensuite, vous pouvez verrouiller le mutex. Tous les threads souhaitant accéder doivent attendre que le thread contenant le mutex le libère.

Il y avait aussi une suggestion d'Apocalisp. Je vous recommanderais également de consulter le package Java.util.concurrent.

1
uriDium

Pour éviter ce message d'erreur, utilisez le mot clé synchronized:

synchronized(serverAddr){
  serverAddr.wait(60000);
}
1
krosenvold

Le code ci-dessous devrait fonctionner.

     private final ReentrantLock lock = new ReentrantLock();

     lock.lock();  // block until condition holds
     try {
        serverAddr.wait(60000);
     } finally {
       lock.unlock()
     }
   }

Reportez-vous à cette documentation page pour plus de détails. 

public void lock()

Acquiert le verrou.

Acquiert le verrou s'il n'est pas détenu par un autre thread et revient immédiatement, en fixant le nombre de verrous à un.

Si le thread en cours détient déjà le verrou, le nombre de mises en attente est incrémenté de un et la méthode retourne immédiatement.

Si le verrou est maintenu par un autre thread, le thread actuel est désactivé pour la planification du thread et reste en sommeil jusqu'à l'acquisition du verrou.

Reportez-vous à cette question SE pour connaître les avantages de lock par rapport à synchronization

Synchronisation vs Verrouillage

0
Ravindra babu

En général, lorsque vous avez un programme multi-thread en Java, vous devez verrouiller la variable partagée en utilisant synchronized (key-Word). À tout moment, un seul thread peut accéder à la mémoire partagée.

0
Khash Nejad