2009-11-27 13 views
28

I einen multithreaded Java-Code haben, in denen:Synchronisation auf der lokalen Variablen

  • mehrere Threads Stateful-Objekte aus dem synchronisierten gemeinsam genutzten Speicher zu lesen (das heißt als ein Ergebnis hiervon können einige Fäden die gleichen Objekte verweisen);
  • jeder Thread ruft dann eine Methode process() auf und übergibt dort sein Objekt;
  • process() verarbeitet Objekte in irgendeiner Weise, was zu einer Änderung des Objektstatus führen kann;
  • Diese Statusänderungen sollten synchronisiert werden.

ich eine Methode, wie das erstellt haben:

public void process(Foo[] foos) { 
    for (final Foo foo : foos) { 
     if (foo.needsProcessing()) { 
      synchronized (foo) { 
       foo.process(); // foo's state may be changed here 
      } 
     } 
    } 
} 

Nach bestem Wissen und Gewissen, das sieht echt. IntelliJs Inspektion beschwert sich jedoch über die Synchronisierung der lokalen Variablen, da "verschiedene Threads sehr wahrscheinlich unterschiedliche lokale Instanzen haben" (dies ist nicht gültig für mich, da ich nicht initialisiere foos in der Methode).

Im Wesentlichen, was ich hier erreichen möchte, ist das gleiche wie Methode Foo.process() synchronisiert zu haben (was keine Option für mich ist, da Foo ein Teil der 3rd Party Library ist).

Ich habe mich an den Code ohne gelbe Markierungen gewöhnt, so dass jeder Rat von der Gemeinschaft geschätzt wird. Ist das wirklich so schlimm, um Synchronisation bei Einheimischen zu machen? Gibt es eine Alternative, die in meiner Situation funktionieren wird?

Vielen Dank im Voraus!

Antwort

21
if (foo.needsProcessing()) { 
    synchronized (foo) { 
     foo.process(); // foo's state may be changed here 
    } 
} 

Ich denke, es eine Race-Bedingung in dem obigen Fragmente ist, die gelegentlich zweimal in foo.process() führen könnten auf demselben Objekt aufgerufen wird. Es sollte sein:

synchronized (foo) { 
    if (foo.needsProcessing()) { 
     foo.process(); // foo's state may be changed here 
    } 
} 

Ist das wirklich so schlimm auf die Einheimischen zu synchronisieren?

Es ist nicht schlecht, auf Einheimische zu synchronisieren per se. Die wirklichen Probleme sind:

  • , ob die verschiedenen Threads auf die richtigen Objekte synchronisieren korrekte Synchronisation zu erreichen, und

  • ob etwas anderes Probleme durch die Synchronisierung für diese Objekte verursachen könnten.

+1

Richtig, danke! BTW, was denkst du über die Hauptfrage hier? Ist das wirklich so schlecht, um auf Einheimische zu synchronisieren? –

1

Refaktorieren Sie den Schleifenkörper in eine separate Methode, indem Sie foo als Parameter verwenden.

+0

Ich muss auch auf diese Methode Parameter zu synchronisieren –

+0

Ja, aber es wird dann keine lokale Variable sein. –

+2

IntelliJ hat eine andere Art von Inspektion "verschiedene Threads haben sehr wahrscheinlich unterschiedliche Methodenparameter" :) –

1

Keine automatische Intelligenz ist perfekt. Ich denke, Ihre Idee, auf der lokalen Variable zu synchronisieren, ist ziemlich korrekt. Also mach es vielleicht (was recht ist) und schlage JetBrains vor, dass sie ihre Inspektion abstimmen sollten.

0

Es ist nichts falsch mit Ihrer Synchronisation auf ein Objekt innerhalb einer Sammlung. Sie könnten versuchen, die foreach mit einem normalen for-Schleife zu ersetzen:

for (int n = 0; n < Foos.length; n++) { 
    Foo foo = Foos[n]; 

    if (null != foo && foo.needsProcessing()) { 
     synchronized (foo) { 
      foo.process(); // foo's state may be changed here 
     } 
    } 
} 

oder sogar (so wird der Detektor nicht über die Foo foo stolpert):

for (int n = 0; n < foos.length; n++) { 
    if (null != foos[n] && foos[n].needsProcessing()) { 
     synchronized (foos[n]) { 
      foos[n].process(); // foos[n]'s state may be changed here 
     } 
    } 
} 

Nicht einen temporären Wert mit dem mehrere zu verhindern foos[n] ist nicht die beste Vorgehensweise, aber wenn es funktioniert, um die unerwünschte Warnung zu verhindern, könnten Sie damit leben. Fügen Sie einen Kommentar hinzu, warum dieser Code eine abweichende Form hat. :-)

2

IDE soll dir helfen, wenn es einen Fehler macht, solltest du dich nicht bücken und es bitte.

Sie können diese Überprüfung in IntelliJ deaktivieren. (Lustig, es ist die einzige standardmäßig unter "Threading-Probleme" aktiviert. Ist dies der häufigste Fehler, den Menschen machen?)

0

In der .NET Welt Objekte tragen manchmal ihre Sperre Objekt als Eigenschaft.

synchronized (foo.getSyncRoot()) { 
    if (foo.needsProcessing()) { 
     foo.process(); // foo's state may be changed here 
    } 
} 

Dies ermöglicht es dem Objekt eine andere Sperrobjekt zu geben, über deren Umsetzung abhängig (zum Beispiel den Monitor an eine zugrunde liegende Datenbankverbindung oder etwas zu delegieren).

4

Stephen C Antwort hat Probleme, er ist unnötig viele Synchronisationssperren Eingabe, seltsam genug, um den besseren Weg, es zu formatieren ist:

public void process(Foo[] foos) { 
     for (final Foo foo : foos) { 
      if (foo.needsProcessing()) { 
       synchronized (foo) { 
        if (foo.needsProcessing()) { 
         foo.process(); // foo's state may be changed here 
        } 
       } 
      } 
     } 
    } 

die Synchronisierungssperre bekommen kann manchmal eine Weile dauern, und wenn es wurde gehalten Etwas änderte etwas. Es könnte etwas sein, das den Status der Verarbeitung von foo in dieser Zeit änderte.

Sie möchten nicht auf die Sperre warten, wenn Sie das Objekt nicht verarbeiten müssen. Und nachdem Sie die Sperre erhalten haben, muss sie möglicherweise noch nicht verarbeitet werden. Auch wenn es irgendwie albern aussieht und Neulinge vielleicht geneigt sind, einen der Checks zu entfernen, ist es tatsächlich der vernünftige Weg, dies zu tun, solange foo.needsProcessing() eine vernachlässigbare Funktion ist.


bringen diese zurück auf die Hauptfrage, wenn es der Fall, dass Sie auf die lokalen Werte in einem Array basiert synchronisieren möchten es ist, weil die Zeit entscheidend ist. Und in diesen Fällen ist das letzte, was Sie tun möchten, jedes einzelne Element im Array zu sperren oder die Daten zweimal zu verarbeiten. Sie würden nur lokale Objekte synchronisieren, wenn Sie ein paar Threads haben, die eine Menge Arbeit verrichten und sehr selten die gleichen Daten berühren müssen, aber sehr wohl.

Dies wird immer nur die Sperre treffen, wenn und nur wenn die Verarbeitung dieses Foo, das Verarbeitung benötigt, Parallelitätsfehler verursachen würde. In den Fällen, in denen Sie nur die präzisen Objekte im Array als solche synchronisieren möchten, wird grundsätzlich eine doppelt getaktete Syntax gewünscht. Dies verhindert, dass die Foos doppelt verarbeitet werden und alle foo, die nicht verarbeitet werden müssen, gesperrt werden.

Sie sind mit dem sehr seltenen Fall des Blockierens des Threads oder sogar nur ein Schloss und genau dann, wenn es darauf ankommt, und Ihr Thread ist nur für genau an dem Punkt blockiert, wo Blockieren nicht zu Parallelitätsfehlern führen würde.

Verwandte Themen