69 votes

Ne rien faire pendant la "condition".

En parcourant le code de la version Java 8 de ForkJoinPool (qui comporte quelques modifications intéressantes par rapport à Java 7), je suis tombé sur cette construction ( aquí ) :

do {} while (!blocker.isReleasable() &&
             !blocker.block());

J'ai du mal à comprendre pourquoi tu l'écris comme ça au lieu de simplement

while (!blocker.isReleasable() &&
       !blocker.block());

S'agit-il simplement d'un choix sémantique/de lisibilité, puisque vous pourriez lire la première construction comme suit do "nothing" while "conditions" ? Ou bien y a-t-il un avantage supplémentaire qui m'échappe ?

54voto

MicSim Points 12980

Si vous lisez les commentaires en haut du fichier, juste en dessous de la déclaration de la classe, il y a une section qui explique l'utilisation de cette construction :

Style notes

===========

[...]

There are several occurrences of the unusual "do {} while
(!cas...)"  which is the simplest way to force an update of a
CAS'ed variable. There are also other coding oddities (including
several unnecessary-looking hoisted null checks) that help
some methods perform reasonably even when interpreted (not
compiled).

45voto

Erik Vesteraas Points 1227

ForkJoinPool fait un usage intensif de compareAndSwap... de sun.misc.Unsafe et la plupart des occurrences de do {} while (...) en ForkJoinPool peut - comme mentionné par d'autres réponses - être expliqué par ce commentaire sous la rubrique Notes de style :

* There are several occurrences of the unusual "do {} while
* (!cas...)"  which is the simplest way to force an update of a
* CAS'ed variable. 

Le choix d'utiliser l'écriture d'un while -boucle avec un corps vide comme do {} while (condition) semble toutefois être un choix essentiellement stylistique. Ceci est peut-être plus clair dans HashMap qui a été mis à jour dans Java 8.

Dans la version de Java 7 HashMap vous pouvez trouver ceci :

while (index < t.length && (next = t[index++]) == null)
    ;

Bien qu'une grande partie du code qui l'entoure ait également changé, il est clair que le remplacement dans Java 8 est le suivant :

do {} while (index < t.length && (next = t[index++]) == null);

La première version présente la faiblesse suivante : si le seul point-virgule était supprimé, la signification du programme serait modifiée en fonction de la ligne suivante.

Comme on le voit ci-dessous, le bytecode généré par while (...) {} y do {} while (...); est légèrement différent, mais pas d'une manière qui devrait affecter quoi que ce soit lors de l'exécution.

Code Java :

class WhileTest {
    boolean condition;

    void waitWhile() {
        while(!condition);
    }

    void waitDoWhile() {
        do {} while(!condition);
    }
}

Code généré :

class WhileTest {
  boolean condition;

  WhileTest();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  void waitWhile();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field condition:Z
       4: ifne          10
       7: goto          0
      10: return        

  void waitDoWhile();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field condition:Z
       4: ifeq          0
       7: return        
}

8voto

Tim B Points 19851

Si l'on laisse de côté les avantages potentiels en termes de performances, il y a un avantage évident en termes de lisibilité.

Con while (X) ; le point-virgule de fin n'est pas toujours évident au premier coup d'œil, vous pouvez être amené à penser que l'instruction ou les instructions suivantes se trouvent à l'intérieur de la boucle. Par exemple :

while (x==process(y));
if (z=x) {
    // do stuff.
}

Il serait très facile de mal interpréter l'exemple ci-dessus en plaçant l'instruction if à l'intérieur de la boucle, et même si vous l'avez lu correctement, il serait facile de penser qu'il s'agit d'une erreur de programmation et que l'instruction if devrait être à l'intérieur de la boucle.

Con do {} while(X); bien qu'il soit immédiatement clair au premier coup d'œil qu'il n'y a pas de corps à la boucle.

4voto

Si vous lisez le commentaire au-dessus du code, il est mentionné que...

Si l'appelant n'est pas un ForkJoinTask cette méthode est comportementalement équivalente à

while (!blocker.isReleasable())
   if (blocker.block())
      return;
}

Il s'agit donc d'une autre forme d'implémentation du code ci-dessus dans une autre partie... !

En Notes de style il est mentionné que,

Il y a plusieurs occurrences de l'inhabituel "do {} while (!cas...)" qui est la manière la plus simple de forcer la mise à jour d'une variable CAS. variable CAS.

Et si vous voyez la mise en œuvre de Verrouillage géré#isReleasable Il met à jour la serrure et retourne true si le blocage n'est pas nécessaire.

Interprétation :

Les boucles while vides sont utilisées pour fournir une interruption jusqu'à ce qu'une certaine condition soit remise à true/false.

Ici, do { } while(!...) est un bloqueur/interrupteur jusqu'à ce que blocker.block() sera true cuando blocker.isReleasable() es false . La boucle continuera l'exécution pendant que blocker n'est pas libérable ( !blocker.isReleasable() ) et blocker n'est pas bloqué ! ! L'exécution sera sortie de la boucle dès que blocker.block() sera mis à true.

Notez que, do{ } while(...) ne met pas à jour la variable CAS, mais il garantit que le programme attendra que la variable soit mise à jour (force à attendre jusqu'à ce que la variable soit mise à jour).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X