In welchen Situationen könnte ein leerer synchronisierten Block korrekte Einfädeln Semantik erreichen?

StackOverflow https://stackoverflow.com/questions/686415

Frage

Ich war auf der Suche durch ein Findbugs Bericht auf meiner Code-Basis und eines des Musters, die ausgelöst wurde, war für ein leerer synchronzied Block (dh synchronized (var) {}). Die Dokumentation sagt :

  

Leere synchronisierten Blöcke sind weit mehr   subtil und schwer richtig zu verwenden als   die meisten Menschen erkennen und leer   synchronisierten Blöcke sind so gut wie nie ein   bessere Lösung als weniger gekünstelt   Lösungen.

In meinem Fall kam es, weil der Inhalt des Blocks wurde aus hatte kommentiert, aber die synchronized Aussage war immer noch da. In welchen Situationen könnte ein leerer synchronized Block korrekte Einfädeln Semantik erreichen?

War es hilfreich?

Lösung

Ein leerer synchronisierten Block wartet, bis niemand sonst diese Synchronizer verwendet. Das mag sein, was Sie wollen, aber weil Sie den nachfolgenden Code in dem synchronisierten Block geschützt haben, ist nichts jemand anderes davon abhalten zu modifizieren, was immer es war man schon lange gewartet haben, während Sie den nachfolgenden Code ausführen. Das ist so gut wie nie, was Sie wollen.

Andere Tipps

Die früheren Antworten nicht das nützlichste, was über leere synchronized Blöcke zu unterstreichen: sie die Sichtbarkeit von Variablen ändert und andere Aktionen über Threads gewährleisten. Wie jtahlborn zeigt an, erlegt Synchronisation ein „Gedächtnis Barriere“ auf dem Compiler, dass es seine Caches zu leeren zwingt und aufzufrischen. Aber ich habe nicht finden, wo „SNAKE diskutiert“ diese, so schrieb ich eine Antwort selbst.

int variable;

void test() // This code is INCORRECT
{
    new Thread( () ->  // A
    {
        variable = 9;
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

Das obige Programm ist falsch. Der Wert der Variablen könnten lokal in Thread A oder B oder beide zwischengespeichert werden. So B könnte nie den Wert von 9 lesen, die A schreibt, und könnte daher Schleife für immer.

Erstellen Sie eine variable Änderung über Threads sichtbar durch die Verwendung leeren synchronized Blöcke

Eine mögliche Korrektur ist ein volatile (effektiv „kein Cache“) Modifikator auf die Variable hinzuzufügen. Manchmal ist dies ineffizient, aber, weil es völlig Caching der Variablen verbietet. Leere synchronized Blöcke, auf der anderen Seite, verbieten nicht-Caching. Alles, was sie tun, ist den Caches zwingt mit dem Hauptspeicher an bestimmten kritischen Punkten zu synchronisieren. Zum Beispiel: *

int variable;

void test() // Corrected version
{
    new Thread( () ->  // A
    {
        variable = 9;
        synchronized( o ) {} // Flush to main memory
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            synchronized( o ) {} // Refresh from main memory
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

final Object o = new Object();

Wie das Speichermodell garantiert Sichtbarkeit

müssen beide Threads auf demselben Objekt, um synchronisieren Sichtbarkeit zu gewährleisten. Diese Garantie beruht auf dem Java Speichermodell , insbesondere auf der Regel, dass eine „Aktion auf dem Monitor m entsperren synchronisiert-mit alle nachfolgenden Sperren Aktionen auf m“ und damit geschieht zuvor diejenigen Aktionen. So Eine der Unlock des Monitors o am Heck seines synchronized Block passiert-vor der anschließenden Schlosses B an der Spitze seines Blocks. (Beachten Sie, dann ist es diese seltsame Schwanz-Kopf Ordnung der Beziehung, die erklärt, warum die Körper leer sein.) Da auch, dass A des Schreibvoraus seine Unlock und B das Schloss voran seine Lese, die Beziehung muss verlängert beiden Schreib abzudecken und lesen: < em> Schreib passiert-vor lesen . Es ist dies von entscheidender Bedeutung, erweiterte Beziehung, die das überarbeitete Programm richtig in Bezug auf das Speichermodell macht.

Ich denke, dies ist die wichtigste Anwendung für leere synchronized Blöcke ist.


* Ich spreche als wäre es eine Frage der Prozessor-Caching weil ich denke, das ist ein hilfreicher Weg, es zu sehen. In Wahrheit, wie Aleksandr Dubinsky hat kommentiert, ‚alle modernen Prozessoren Cache-kohärente. Das geschieht vor-Beziehung ist mehr darüber, was der Compiler erlaubt ist eher zu tun, als die CPU.‘

Früher war es der Fall, dass die Spezifikation impliziert bestimmte Speichersperroperationen aufgetreten. Allerdings hat die Spezifikation jetzt geändert und die ursprüngliche Spezifikation wurde nie richtig umgesetzt. Es kann verwendet werden, für einen anderen Thread zu warten, um die Verriegelung zu lösen, aber die Koordination dass der andere Thread erworben hat bereits die Sperre wäre schwierig.

Synchronisieren tut ein bisschen mehr als nur darauf warten, während unelegant Codierung dies den Effekt erzielen konnte.

http://www.javaperformancetuning.com/news/qotm030.shtml

  
      
  1. Der Faden erhält die Sperre auf dem Monitor für das Objekt diese (unter der Annahme des Monitors entriegelt wird, da sonst die Thread wartet, bis der Monitor entriegelt ist).
  2.   
  3. Der Thread-Speicher alle seine Variablen spült, dh er alle seine Variablen effektiv lesen von „main“ Speicher (JVMs schmutzig Sätze verwenden kann dies zu optimieren, so dass nur „schmutziges“ Variablen gespült werden, aber vom Konzept her ist dies die gleiche Abschnitt 17.9 der Java-Sprachspezifikation.) Siehe.
  4.   
  5. Der Codeblock ausgeführt (in diesem Fall den Rückgabewert auf den aktuellen Wert von i3 Einstellung, die nur aus „main“ Speicher zurückgesetzt worden sein kann).
  6.   
  7. (Alle Änderungen an Variablen normalerweise jetzt zu „main“ Speicher geschrieben werden würde, aber für geti3 () haben wir keine Änderungen.)
  8.   
  9. Der Faden hebt die Sperre auf dem Monitor für Objekt dieser.
  10.   

Für eine tiefer gehende Blick in die Java-Speichermodell, haben einen Blick auf dieses Video von Google ‚Weiterführende Themen in Programmiersprachen‘ Serie: http://www.youtube.com/watch?v=1FX4zco0ziY

Es gibt einen wirklich schönen Überblick darüber, was der Compiler (oft in der Theorie, aber manchmal in der Praxis), um Ihren Code zu tun. Wesentliche Sachen für jeden ernsthaften Java-Programmierer!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top