Ne faites rien en condition

En parcourant le code de la version Java 8 de ForkJoinPool (qui a quelques modifications intéressantes de Java 7), j’ai parcouru cette construction ( ici ):

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

Je me bats avec pourquoi vous l’écririez comme ça au lieu de simplement

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

Est-ce juste un choix sémantique / lisibilité, puisque vous pouvez lire le premier construit comme do "nothing" while "conditions" ? Ou y a-t-il un avantage supplémentaire qui me manque?

Si vous lisez les commentaires en haut du fichier, juste en dessous de la déclaration de 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 comstackd). 

ForkJoinPool utilise intensivement compareAndSwap... partir de sun.misc.Unsafe et la plupart des occurrences de do {} while (...) dans ForkJoinPool peuvent – comme mentionné par d’autres réponses – être expliquées par ce commentaire sous le titre 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 une boucle while avec un corps vide comme do {} while (condition) semble cependant être un choix principalement stylistique. C’est peut-être plus clair dans HashMap , qui a été mis à jour dans Java 8.

Dans Java HashMap vous pouvez trouver ceci:

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

Bien qu'une grande partie du code autour de lui 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 a la faiblesse que si le seul point-virgule était supprimé, cela changerait la signification du programme en fonction de la ligne suivante.

Comme ci-dessous, bytecode généré par while (...) {} et do {} while (...); est légèrement différent, mais ne devrait en aucun cas 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."":()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 } 

Sans compter les avantages potentiels en termes de performance, il existe un avantage évident en termes de lisibilité.

Avec while (X) ; le point-virgule final n’est pas toujours évident à première vue, vous pouvez être dérouté en pensant que l’instruction ou les instructions suivantes sont dans la boucle. Par exemple:

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

Il serait très facile de mal interpréter ce qui précède comme ayant l’instruction if dans la boucle, et même si vous le lisiez correctement, il serait facile de penser que c’était une erreur de programmation et que le if devrait se trouver dans la boucle.

Avec do {} while(X); cependant, il est immédiatement évident qu’il n’y a pas de corps dans la boucle.

Si vous lisez un commentaire au dessus du code, il est mentionné que …

Si l’appelant n’est pas un ForkJoinTask , cette méthode est équivalente sur le plan comportemental à

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

Donc, c’est juste une autre forme pour implémenter le code ci-dessus dans une autre partie … !!

Dans les notes de style, il est mentionné que,

Il existe plusieurs occurrences de l’inhabituel “do {} while (! Cas …)”, qui est le moyen le plus simple de forcer la mise à jour d’une variable CAS.

Et si vous voyez l’implémentation de ManagedLocker # isReleasable , il met à jour le verrou et renvoie true si le blocage est inutile.

Interprétation :

Les boucles while sont utilisées pour fournir une interruption jusqu’à ce que certaines conditions reviennent à true / false.

Ici, do { } while(!...) est un bloqueur / interruption jusqu’à blocker.block() que blocker.block() soit true lorsque blocker.isReleasable() est false . La boucle continuera son exécution alors que le blocker n’est pas libérable ( !blocker.isReleasable() ) et que le blocker n’est pas bloqué !! L’exécution sera bouclée dès que blocker.block() sera défini sur true.

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

Vous pouvez facilement faire quelque chose comme ça avec:

 if(true){ //Do nothing ... }