2015-04-16 2 views
1

Ma compréhension des blocs Java synchronized() est que, si un thread possède déjà un verrou sur un objet, il peut entrer un bloc différent synchronisé sur le même objet (synchronisation rentrante) . En dessous, je crois que la JVM utilise un nombre de références pour incrémenter/décrémenter le nombre de fois qu'un thread a acquis un verrou, et que le verrou n'est libéré que lorsque le compte est nul.Java Wait/Notifier dans les blocs synchronisés réentrants

Donc ma question est, si l'on rencontre un morceau de code qui ressemble à ceci:

synchronized(this) 
{ 
    if (condition == WAITING) 
    { 
     synchronized(this) 
     { 
      condition = COMPLETE; 

      notify(); 

      try 
      { 
       wait(); 
      } 
      catch(InterruptedException e) 
      { 
      } 
     } 
    } 
    else 
     condition = READY; 
} 

ce qui se passe surtout quand attente() est appelée? Est-ce que cela ne fait que décrémenter le compte, ou est-ce qu'il libère le verrou quel que soit le nombre? Dans le premier cas, il me semble que cela produira un interblocage si une rentrée de verrouillage s'est produite, car il possédera toujours le verrou et attendra donc toujours sur un autre thread qui l'attend.

Dans le second cas, je ne vois pas vraiment quel est le point du second bloc synchronisé.

La documentation attente() dit

« Le thread courant doit posséder le moniteur de cet objet. Le fil libère la propriété de ce moniteur et attend jusqu'à ce qu'un autre fil informe les threads en attente sur l'écran de cet objet pour se réveiller soit par un appel à la notifier ou la méthode de notifyAll. le fil attend alors jusqu'à ce qu'il puisse réobtenir la propriété du moniteur et reprend l'exécution, »

donc je pense que le second cas est correcte, mais Je peux me tromper. Est-ce qu'il me manque quelque chose, ou est-ce que je viens juste de rencontrer un bloc synchronisé redondant qui pourrait tout aussi bien être retiré du code?

+0

Si vous voyez un code comme celui-ci (dont certains ne sont pas compilés), je vous suggère de le supprimer et de le réécrire. Note: la première fois que vous appelez notifier, rien ne se passera car aucun thread ne sera en attente. La deuxième fois, vous n'appelez pas notifier car la condition a changé, donc votre premier thread attendra pour toujours. –

+0

J'ai (mal) édité le code pour le simplifier à titre d'exemple. La condition et les erreurs incompilables étaient moi, pas le code/problème lui-même. En outre, la condition est mise à jour ailleurs, par un autre thread. Le jist de base du code est pour un thread en attente d'un autre thread à compléter pour être averti que la condition a changé. Il appelle alors notifier ailleurs quand il a fini. – user2725525

+1

Pourquoi ne pas le réécrire pour utiliser un Futures afin de savoir quand un thread est terminé? –

Répondre

6

Il n'y a rien qui nécessite la réacquisition de la serrure après le if.

Le wait() libérera également le verrou complètement (sinon il serait tout à fait enclin à l'impasse).

La seule raison pour le deuxième synchronized que je peux voir, c'est qu'il a précédemment utilisé un autre objet et quelqu'un l'a modifié pour utiliser le même this par erreur.

+0

C'était aussi mon hypothèse. Je suis en train de porter une base de code Java assez grande en C++, et en essayant de la refactoriser pour éviter d'utiliser std :: recursive_mutex. Merci pour l'information! – user2725525

+0

Juste pour confirmer ce scénario: lorsque le thread rencontre le bloc synchronisé externe, il va acquérir un verrou sur l'objet. Il rencontre alors le deuxième bloc synchronisé qui va déclencher la réentrée. Notify est appelé mais aucun thread ne peut acquérir un verrou pour le moment car le thread actuel a toujours l'objet verrouillé. Enfin, wait est appelée, ce qui fait que le thread en cours renonce au verrou qu'il contient. Êtes-vous d'accord? –

+0

Exactement. Cela ressemble aussi à une construction qui pourrait être manipulée beaucoup mieux avec une classe de 'java.util.concurrent', mais je suppose que le code est vieux et l'auteur peut ne pas être familier avec eux. – Kayaman