Beim Durchsuchen des Codes für die Java 8-Version von ForkJoinPool (die einige interessante Änderungen gegenüber Java 7 enthält) bin ich auf dieses Konstrukt gestoßen ( hier ):
do {} while (!blocker.isReleasable() &&
!blocker.block());
Ich habe Probleme damit, warum Sie es so schreiben würden, anstatt nur
while (!blocker.isReleasable() &&
!blocker.block());
Ist es nur eine Entscheidung für Semantik/Lesbarkeit, da Sie das erste Konstrukt als do "nothing" while "conditions"
lesen können? Oder fehlt mir ein zusätzlicher Nutzen?
Wenn Sie die Kommentare oben in der Datei direkt unter der Klassendeklaration lesen, gibt es einen Abschnitt, in dem die Verwendung dieses Konstrukts erläutert wird:
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).
ForkJoinPool
verwendet compareAndSwap...
von Sun.misc.Unsafe
ausgiebig, und die meisten Vorkommen von do {} while (...)
in ForkJoinPool
können - wie in anderen Antworten erwähnt - durch diesen Kommentar unter der Überschrift Stilanmerkungen erklärt werden:
* There are several occurrences of the unusual "do {} while * (!cas...)" which is the simplest way to force an update of a * CAS'ed variable.
Die Wahl, eine while
- Schleife mit einem leeren Körper als do {} while (condition)
zu verwenden, scheint jedoch eine hauptsächlich stilistische Wahl zu sein. Dies ist vielleicht in HashMap
klarer, das in Java 8 aktualisiert wurde.
In Java 7 HashMap
finden Sie folgendes:
while (index < t.length && (next = t[index++]) == null)
;
Auch wenn sich ein Großteil des Codes geändert hat, ist der Ersatz in Java 8 eindeutig:
do {} while (index < t.length && (next = t[index++]) == null);
Die erste Version hat die Schwäche, dass, wenn das einsame Semikolon gelöscht wurde, die Bedeutung des Programms in Abhängigkeit von der folgenden Zeile geändert wird.
Wie unten zu sehen ist, ist der von while (...) {}
und do {} while (...);
generierte Bytecode etwas anders, jedoch nicht in irgendeiner Weise, die irgendetwas beeinflussen sollte, wenn er ausgeführt wird.
Java-Code:
class WhileTest {
boolean condition;
void waitWhile() {
while(!condition);
}
void waitDoWhile() {
do {} while(!condition);
}
}
Generierter Code:
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
}
Abgesehen von möglichen Leistungsvorteilen ergibt sich ein klarer Lesbarkeitsvorteil.
Bei while (X) ;
ist das nachgestellte Semikolon auf den ersten Blick nicht immer offensichtlich. Sie könnten verwirrt sein, wenn Sie denken, dass die folgenden Anweisungen innerhalb der Schleife liegen. Zum Beispiel:
while (x==process(y));
if (z=x) {
// do stuff.
}
Es wäre sehr leicht, das obige als falsch zu interpretieren, wenn man die if-Anweisung in der Schleife hat. Selbst wenn Sie es richtig gelesen haben, ist es leicht zu glauben, dass es sich um einen Programmierfehler handelt und das if sich innerhalb der Schleife befinden sollte.
Mit do {} while(X);
ist jedoch auf einen Blick sofort klar, dass sich kein Körper in der Schleife befindet.
Wenn Sie den Kommentar über dem Code lesen, wird erwähnt, dass ...
Wenn der Aufrufer keine ForkJoinTask
ist, entspricht diese Methode dem Verhalten
while (!blocker.isReleasable())
if (blocker.block())
return;
}
Es ist also nur ein anderes Formular, um den Code in anderen Teil zu implementieren ... !!
In Style-Notizen es wird erwähnt, dass
Es gibt mehrere Vorkommen des ungewöhnlichen "do {} while (! Cas ...)". Dies ist der einfachste Weg, ein Update von .__ zu erzwingen. CAS-Variable.
Und wenn Sie die Implementierung von ManagedLocker # isReleasable sehen, aktualisiert es die Sperre und gibt true
zurück, wenn das Blockieren nicht erforderlich ist.
Deutung :
Leere while-Schleifen werden verwendet, um einen Interrupt bereitzustellen, bis eine Bedingung auf true/false zurückgesetzt wird.
Hier ist do { } while(!...)
ein Blocker/Interrupt, bis blocker.block()
true
ist, wenn blocker.isReleasable()
false
ist. Die Schleife setzt die Ausführung fort, während blocker
nicht freigegeben werden kann (!blocker.isReleasable()
) und blocker
nicht blockiert ist !! Die Ausführung wird außerhalb der Schleife ausgeführt, sobald blocker.block()
auf true gesetzt ist.
Beachten Sie, dass do{ } while(...)
die CAS-Variable nicht aktualisiert, sie garantiert jedoch, dass das Programm wartet, bis die Variable aktualisiert wird (zwingen, zu warten, bis die Variable aktualisiert wird).
Sie können so etwas leicht machen mit:
if(true){
//Do nothing ...
}