Tun Sie "nichts", während "Zustand"

70

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 Wahl der Semantik / Lesbarkeit, da Sie das erste Konstrukt als lesen könnten do "nothing" while "conditions"? Oder fehlt mir ein zusätzlicher Vorteil?

Erik Vesteraas
quelle

Antworten:

54

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).
MicSim
quelle
25
Da ich selbst eine Erinnerung brauchte: CAS ist en.wikipedia.org/wiki/Compare-and-swap
Erik Vesteraas
14
Möglicherweise haben Sie erklärt, wie das Konstrukt hilft, "eine Aktualisierung einer CAS-Variablen zu erzwingen".
Honza Zidek
7
Aber warum verursacht es im Gegensatz zur whileVersion ein Update einer CAS-Variablen ?
Benjamin Gruenbaum
4
Es heißt nicht, ob do {} while (Bedingung) vs. while (Bedingung) do; macht einen Unterschied. Wenn es einen Unterschied macht, dann ist dies (a) ein schrecklich schlechter Kommentar, (b) ich halte es für einen Fehler , den Kommentar nicht in der Aussage selbst zu haben, und (c) die Tatsache, dass es einen Unterschied macht, ist eine wichtige WTF in die Sprache.
Gnasher729
3
Dies klingt nach tiefer Magie, bei der durch die Änderung des Bereichs do {} while()eine JIT-Optimierung vermieden wird, die von den Benutzern while()verwendet wird. Die while-Anweisungen sind semantisch äquivalent, daher muss der Unterschied in der Implementierung der JVM liegen.
Michael Shopsin
45

ForkJoinPoolnutzt compareAndSwap...aus sun.misc.Unsafeund die meisten Vorkommen von do {} while (...)in ForkJoinPoolkönnen - wie in anderen Antworten erwähnt - durch diesen Kommentar unter der Überschrift Style Notes 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- whileSchleife mit einem leeren Körper zu schreiben, do {} while (condition)scheint jedoch eine meist stilistische Wahl zu sein. Dies ist vielleicht klarer in HashMap, was zufällig in Java 8 aktualisiert wurde.

In Java 7 finden HashMapSie Folgendes:

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

Während sich ein Großteil des Codes geändert hat, ist klar, dass der Ersatz in Java 8 folgender ist:

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

Die erste Version hat die Schwäche, dass das Löschen des einzelnen Semikolons die Bedeutung des Programms abhängig von der folgenden Zeile ändern würde.

Wie unten zu sehen ist, unterscheidet sich der von while (...) {}und generierte Bytecode do {} while (...);geringfügig, darf sich jedoch in keiner Weise auf die Ausführung auswirken.

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        
}
Erik Vesteraas
quelle
3
Ich komme von einer doppelten Frage hierher : Diese IMHO ist eine weitaus bessere und überzeugendere Antwort als die derzeit akzeptierte.
Marco13
8

Abgesehen von potenziellen Leistungsvorteilen ergibt sich ein klarer Vorteil für die Lesbarkeit.

Da while (X) ;das nachfolgende Semikolon auf den ersten Blick nicht immer offensichtlich ist, können Sie verwirrt sein, wenn Sie glauben, dass sich die folgenden Anweisungen in der Schleife befinden. Zum Beispiel:

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

Es wäre sehr leicht, das Obige falsch zu verstehen, wenn die if-Anweisung in der Schleife enthalten wäre, und selbst wenn Sie es richtig gelesen hätten, wäre es leicht zu glauben, dass es sich um einen Programmierfehler handelt und das if in der Schleife sein sollte.

Dabei do {} while(X);ist sofort auf einen Blick klar, dass die Schleife keinen Körper hat.

Tim B.
quelle
Warum nicht einfach schreiben while (x==process(y)) {}? Ich denke, das wäre fast so klar wie die do ... while()Lösung.
Exilit
@exilit das würde helfen, aber da die do {} whileKonstruktion Schlüsselwörter zwischen den Klammern enthält, wird die Trennung sauberer. Selbst in diesem einfachen Beispiel haben wir bereits 4 Klammern nacheinander )){}- Sie können leicht erkennen, dass diese Zahl zunehmen könnte, wenn sie komplizierter wird {}.
Tim B
4

Wenn Sie einen Kommentar über dem Code lesen, wird erwähnt, dass ...

Wenn der Aufrufer kein a ist ForkJoinTask, entspricht diese Methode verhaltensmäßig

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

Es ist also nur eine andere Form, den obigen Code in einem anderen Teil zu implementieren ... !!

In Style Notes wird erwähnt, dass,

Es gibt mehrere Vorkommen des ungewöhnlichen "do {} while (! Cas ...)", das die einfachste Möglichkeit ist, eine Aktualisierung einer CAS-Variablen zu erzwingen.

Wenn die Implementierung von ManagedLocker # isReleasable angezeigt wird , wird die Sperre aktualisiert und zurückgegeben, truewenn keine Blockierung erforderlich ist.

Interpretation :

Leer, während Schleifen verwendet werden, um einen Interrupt bereitzustellen, bis eine Bedingung auf wahr / falsch zurückgesetzt wird.

Hier do { } while(!...)ist ein Blocker / Unterbrechung , bis blocker.block()sein wird , truewenn blocker.isReleasable()ist false. Die Schleife setzt die Ausführung fort, solange sie blockernicht freigebbar ist ( !blocker.isReleasable()) und blockernicht blockiert ist !! Die Ausführung wird außerhalb der Schleife sein, sobald blocker.block()sie auf true gesetzt ist.

Beachten Sie, dass do{ } while(...)die CAS-Variable nicht aktualisiert wird, aber garantiert, dass das Programm wartet, bis die Variable aktualisiert wird (erzwingen Sie, bis die Variable aktualisiert wird).

Kein Fehler
quelle
Ja, aber in beiden Fällen wird das Konstrukt do {} while (Bedingung) verwendet. Der Kommentar ist also in der Tat nur eine dritte Möglichkeit, dasselbe zu schreiben.
Erik Vesteraas
@ErikVesteraas aktualisiert die Antwort, Es war in Bearbeitung, als Sie kommentierten: D
Kein Fehler
Bedeutet der Kommentar in den Stilnotizen also, dass while(!cas...)keine Aktualisierung erzwungen werden würde? Dass es einen tatsächlichen technischen Unterschied gibt, wie der Bytecode zeigt? Ich frage mich, ob das Betrachten von isReleasable eine Ablenkung ist, da es viele Stellen gibt, an denen es für tatsächliche CAS-Variablen wie diese verwendet wird:do {} while (!U.compareAndSwapLong(...));
Erik Vesteraas
@ErikVesteraas Ich glaube nicht, dass isReleasable()es eine Ablenkung ist. Siehe die Interpretation bearbeiten.
Kein Fehler
2
Ja, ich stimme dem zu, was Sie geschrieben haben, aber es wird nicht while(!cas...);angesprochen, ob eine Aktualisierung einer CompareAndSwap-Variablen erzwungen werden kann oder ob dies do {} while (!cas...);erforderlich ist, dh ob es einen tatsächlichen technischen Unterschied gibt oder nicht.
Erik Vesteraas
0

Sie können so etwas ganz einfach machen mit:

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