2016-11-04 2 views
0

J'utilise wait() et notify() dans mon code et il existe un scénario où l'exécution peut atteindre notify() avant qu'une attente ne soit réellement appelée sur l'objet lock sur un autre thread. J'ai créé un drapeau "isWaitCalled" dans l'objet lock et l'utilise pour ignorer l'appel notify() si wait() n'est pas déjà appelé. Ce drapeau "isWaitCalled" est-il redondant? Est-ce correct si notify() est appelé avant d'attendre dans l'un des scénarios? Discussion A:Est-il acceptable d'appeler notify() sur un objet lock avant d'appeler wait()?

synchronized (syncObject) { 
      try { 
       if (!syncObject.isLoadComplete) { 
        syncObject.isWaitCalled = true; 
        syncObject.wait(); 
       } 
      } catch (Exception ex) { 
      }            
    } 

ThreadB:

synchronized (syncObject) { 
      try { 
       if (!syncObject.isLoadComplete) { 
        syncObject.isLoadComplete =true; 
        if (syncObject.isWaitCalled) { 
         syncObject.notify();   
        }     
       } 
      } catch (Exception ex) { 
      }            
    } 
+0

Si un arbre tombe dans une forêt, mais personne n'est là pour l'entendre, est-il un son? – erickson

+0

C'est ce que je pensais aussi. Je voulais confirmer et je ne voulais pas prendre. – arun8

Répondre

3

Il n'y a pas de problème particulier avec l'appel notify() ou notifyAll() sur un objet sur lequel aucun thread n'attend. En particulier, il ne bloquera pas, et dans ce sens la variable isWaitCalled ne sert à rien. Notez toutefois que les notifications n'ont d'effet que sur les threads qui sont déjà en attente à la réception de la notification. En particulier, un thread qui invoque la méthode wait() de l'objet après le notify() se bloque jusqu'à ce que le suivant notifie. Pour cette raison, le paradigme wait/notify standard requiert que les threads vérifient avant d'attendre s'ils doivent attendre.

Dans sa forme la plus simple, les threads vérifieraient la valeur de certaines variables partagées pour déterminer si elles devaient attendre et si, après avoir attendu d'attendre, elles devaient reprendre l'attente. Le thread notifiant, quant à lui, est alors responsable de modifier cette variable de manière appropriée avant d'envoyer sa notification. C'est plus ou moins l'inverse de ce que vous présentez.

0

D'une certaine façon, vous devez savoir s'il y a quelque chose à attendre ou non. Sinon, vous n'avez aucun moyen de savoir s'il faut appeler wait. Cette chose que vous attendez s'appelle le "prédicat". Dans votre cas, isLoadComplete est déjà le prédicat, donc avoir un autre drapeau qui suit si vous devez attendre ou non est redondant.

Vous devez attendre si, et seulement si, isLoadComplete est . Vous appelez notify lorsque, et seulement quand, vous avez défini isLoadComplete à true. Qu'est-ce qui te fait penser que tu as besoin de plus que ça?

L'appel notify lorsqu'aucun thread n'attend est inoffensif.

2

Est-il acceptable d'appeler notify() sur un objet lock avant d'appeler wait()?

Eh bien, cela dépend du contexte. À première vue, cela ne fait aucun mal.

Toutefois, si vous codez dépend du thread qui appelle wait() voyant la notification spécifique, il ne fonctionnera pas. Les notifications ne sont pas mises en file d'attente. S'il n'y avait rien d'attente lorsque l'appel notify() se produit, la notification est ignorée.

Cependant, votre code est cassé pour d'autres raisons.

  1. Java wait/notify peut produire de fausses notifications; c'est à dire.un fil dans wait() peut être réveillé sans un notify() ou notifyAll() spécifique. Votre code suppose que loadComplete a été défini à la suite du retour du wait() dans le thread A. Il ne peut pas supposer que ... à cause de wakeups parasites.

  2. Vous supposez qu'un seul thread peut attendre la fin de la charge. Si ce n'est pas vrai, alors un thread sera réveillé, et le reste pourrait être bloqué pour toujours. Enfin, attraper et jeter Exception comme cela est faux sur deux points.

    • Vous interceptez/annulez toute autre exception cochée ou non cochée (à l'exception de Error, etc.).
    • Vous ignorez l'exception vérifiée que vous doit être traitant. Si le thread A ou le thread B reçoit une interruption à un moment malencontreux, alors les choses vont se casser. En particulier le thread A pourrait penser que le code a chargé quand il n'a pas. Si vous n'avez pas l'intention de traiter les interruptions, alors les traiter comme un cas «cela ne devrait jamais arriver» ... et lancer un AssertionError, ou quelque chose d'équivalent.

Je vous conseille d'utiliser le modèle standard pour la mise en œuvre d'une variable de condition qui est présentée dans les java.lang.Object javadocs. Utilisez une boucle, et n'essayez pas d'optimiser le cas de notifications inutiles. Et si vous ressentez le besoin d'optimiser, essayez d'utiliser l'une des constructions d'accès concurrentes de niveau supérieur (par exemple un «verrou») de préférence à l'implémentation de votre propre solution. Si vous êtes «créatif», vous pouvez avoir des ennuis. Et même si votre code est parfait, vous ferez du travail supplémentaire pour les gars qui conservent votre code. S'ils se heurtent à de mystérieux bogues concurrentiels, ils devront peut-être (ré) analyser votre solution «créative» à partir des premiers principes pour déterminer si elle est réellement saine.

Voici comment devrait implémenter ceci. Comme vous pouvez le voir, il y a moins de code que votre solution, et il est plus facile à comprendre:

ThreadA:

synchronized (syncObject) { 
     try { 
      // The loop deals with spurious wakeups. 
      while (!syncObject.isLoadComplete) { 
       syncObject.wait(); 
      } 
     } catch (InterruptedException ex) { 
      // This is the "easy" way to do it correctly, in the case 
      // where interrupts are not designed for. 
      throw AssertionError("interrupted unexpectedly"); 
     }            
    } 

ThreadB:

synchronized (syncObject) { 
     if (!syncObject.isLoadComplete) { 
      syncObject.isLoadComplete = true; 
      syncObject.notifyAll(); // use `notify()` only if threadA 
            // is guaranteed to be the only waiter. 
     }            
    } 
0

Lorsque vous vous trouvez en utilisant la

wait()

méthode, et que l'appel de méthode n'est pas dans une boucle, y Vous faites presque certainement quelque chose de mal.

la boucle doit être de la forme

while (_whatever_im_waiting_for_to_happen_has_not_happened_) 
    wait(); 
}