web-dev-qa-db-de.com

Mach "nichts" während "Bedingung"

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?

69
Erik Vesteraas

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).
54
MicSim

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        
}
45
Erik Vesteraas

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.

8
Tim B

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). 

4
Not a bug

Sie können so etwas leicht machen mit:

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