Was ist das häufigste Parallelitätsproblem, auf das Sie in Java gestoßen sind? [geschlossen]

191

Dies ist eine Art Umfrage zu häufigen Parallelitätsproblemen in Java. Ein Beispiel könnte der klassische Deadlock oder Race-Zustand sein oder vielleicht EDT-Threading-Fehler in Swing. Ich interessiere mich sowohl für eine Vielzahl möglicher Probleme als auch für die am häufigsten auftretenden Probleme. Bitte hinterlassen Sie pro Kommentar eine bestimmte Antwort auf einen Java-Parallelitätsfehler und stimmen Sie ab, wenn Sie eine Antwort sehen, auf die Sie gestoßen sind.

Alex Miller
quelle
16
Warum ist das geschlossen? Dies ist sowohl für andere Programmierer nützlich, die in Java um Parallelität bitten, als auch um eine Vorstellung davon zu haben, welche Klassen von Parallelitätsfehlern von anderen Java-Entwicklern am häufigsten beobachtet werden.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
@Longpoke Die Abschlussmeldung erklärt, warum es geschlossen ist. Dies ist keine Frage mit einer bestimmten "richtigen" Antwort, sondern eher eine Umfrage- / Listenfrage. Und Stack Overflow beabsichtigt nicht, solche Fragen zu beantworten. Wenn Sie mit dieser Richtlinie nicht einverstanden sind, möchten Sie sie möglicherweise auf Meta diskutieren .
Andrzej Doyle
7
Ich denke, die Community ist anderer Meinung, da dieser Artikel mehr als 100 Aufrufe pro Tag erhält! Ich fand es sehr nützlich, da ich an der Entwicklung eines statischen Analysetools beteiligt bin, das speziell zur Behebung von Parallelitätsproblemen entwickelt wurde. Contemplateltd.com/threadsafe . Eine Bank mit häufig auftretenden Parallelitätsproblemen war großartig, um ThreadSafe zu testen und zu verbessern.
Craig Manson
Die Checkliste für die Codeüberprüfung für Java Concurrency verarbeitet die meisten der in den Antworten auf diese Frage genannten Fallstricke in einer Form, die für die tägliche Codeüberprüfung geeignet ist.
Leventov

Antworten:

125

Das häufigste Parallelitätsproblem, das ich gesehen habe, besteht darin, nicht zu erkennen, dass ein von einem Thread geschriebenes Feld nicht garantiert von einem anderen Thread gesehen wird. Eine häufige Anwendung davon:

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    while(!stop) {
      doSomeWork();
    }
  }

  public void setStop() {
    this.stop = true;
  }
}

Solange Anschlag ist nicht flüchtig oder setStopund runnicht sind synchronisiert diese nicht zur Arbeit gewährleistet ist. Dieser Fehler ist besonders teuflisch, da er in 99,999% in der Praxis keine Rolle spielt, da der Leser-Thread die Änderung irgendwann sehen wird - aber wir wissen nicht, wie schnell er sie gesehen hat.

Kutzi
quelle
9
Eine gute Lösung hierfür besteht darin, die Variable stop instance zu einem AtomicBoolean zu machen. Es löst alle Probleme des Nichtflüchtigen und schützt Sie gleichzeitig vor den JMM-Problemen.
Kirk Wylie
39
Es ist schlimmer als "für einige Minuten" - Sie könnten es NIE sehen. Unter dem Speichermodell kann die JVM while (! Stop) in while (true) optimieren, und dann werden Sie abgespritzt. Dies kann nur auf einigen VMs geschehen, nur im Servermodus, nur wenn die JVM nach x Iterationen der Schleife usw. neu kompiliert wird. Autsch!
Cowan
2
Warum sollten Sie AtomicBoolean anstelle von flüchtigem Boolean verwenden? Ich entwickle für Version 1.4+. Gibt es also Fallstricke, wenn ich nur flüchtig deklariere?
Pool
2
Nick, ich denke, das liegt daran, dass atomares CAS normalerweise sogar schneller als flüchtig ist. Wenn Sie für 1.4 entwickeln, besteht Ihre einzige sichere Option meiner Meinung nach darin, synchronisiert als flüchtig in 1.4 zu verwenden. Es verfügt nicht über die Garantien für eine starke Speicherbarriere wie in Java 5.
Kutzi
5
@ Thomas: Das liegt am Java-Speichermodell. Sie sollten darüber lesen, wenn Sie es im Detail wissen möchten (Java Concurrency in Practice von Brian Goetz erklärt es zB gut). Kurz gesagt: Wenn Sie keine Schlüsselwörter / Konstrukte für die Speichersynchronisierung verwenden (wie flüchtig, synchronisiert, AtomicXyz, aber auch wenn ein Thread fertig ist), hat ein Thread KEINE Garantie dafür, dass Änderungen an einem Feld vorgenommen werden, die von einem anderen Thread vorgenommen wurden
Kutzi,
178

Mein schmerzhaftestes Parallelitätsproblem Nr. 1 trat jemals auf, als zwei verschiedene Open Source-Bibliotheken so etwas taten:

private static final String LOCK = "LOCK";  // use matching strings 
                                            // in two different libraries

public doSomestuff() {
   synchronized(LOCK) {
       this.work();
   }
}

Auf den ersten Blick sieht dies wie ein ziemlich triviales Synchronisationsbeispiel aus. Jedoch; Da Strings in Java interniert sind , "LOCK"stellt sich heraus, dass der Literal-String dieselbe Instanz von ist java.lang.String(obwohl sie völlig unterschiedlich voneinander deklariert sind). Das Ergebnis ist offensichtlich schlecht.

Jared
quelle
62
Dies ist einer der Gründe, warum ich privates statisches Endobjekt LOCK = new Object () bevorzuge;
Andrzej Doyle
17
Ich liebe es - oh, das ist böse :)
Thorbjørn Ravn Andersen
7
Das ist gut für Java Puzzlers 2.
Dov Wasserman
12
Eigentlich ... möchte ich wirklich, dass der Compiler sich weigert, die Synchronisierung auf einem String zuzulassen. Angesichts der String-Internierung gibt es keinen Fall, in dem dies eine "gute Sache (tm)" wäre.
Jared
3
@Jared: "bis der String interniert ist" macht keinen Sinn. Saiten "werden" nicht auf magische Weise interniert. String.intern () gibt ein anderes Objekt zurück, es sei denn, Sie haben bereits die kanonische Instanz des angegebenen Strings. Außerdem werden alle Literalzeichenfolgen und konstanten Ausdrücke mit Zeichenfolgenwert interniert. Immer. Siehe die Dokumente für String.intern () und §3.10.5 des JLS.
Laurence Gonsalves
65

Ein klassisches Problem besteht darin, das zu synchronisierende Objekt während der Synchronisierung zu ändern:

synchronized(foo) {
  foo = ...
}

Andere gleichzeitige Threads werden dann für ein anderes Objekt synchronisiert, und dieser Block bietet nicht den erwarteten gegenseitigen Ausschluss.

Alex Miller
quelle
19
Hierfür gibt es eine IDEA-Prüfung mit dem Namen "Synchronisation in einem nicht endgültigen Feld, bei dem es unwahrscheinlich ist, dass sie eine nützliche Semantik aufweist". Sehr schön.
Jen S.
8
Ha ... das ist eine gequälte Beschreibung. "Es ist unwahrscheinlich, dass es eine nützliche Semantik gibt" könnte besser als "höchstwahrscheinlich kaputt" beschrieben werden. :)
Alex Miller
Ich denke, es war Bitter Java, das dies in seinem ReadWriteLock hatte. Zum Glück haben wir jetzt java.util.concurrency.locks und Doug ist ein bisschen mehr am Ball.
Tom Hawtin - Tackline
Ich habe dieses Problem auch oft gesehen. Übrigens nur für endgültige Objekte synchronisieren. FindBugs et al. Hilfe, ja.
Gimpf
Ist dies nur ein Problem während der Zuordnung? (Siehe das Beispiel von @Alex Miller unten mit einer Karte.) Würde dieses Kartenbeispiel auch das gleiche Problem haben?
Alex Beardsley
50

Ein häufiges Problem ist die Verwendung von Klassen wie Calendar und SimpleDateFormat aus mehreren Threads (häufig durch Zwischenspeichern in einer statischen Variablen) ohne Synchronisierung. Diese Klassen sind nicht threadsicher, sodass Multithread-Zugriff letztendlich seltsame Probleme mit inkonsistentem Status verursacht.

Alex Miller
quelle
Kennen Sie ein Open Source-Projekt, das diesen Fehler in einer Version davon enthält? Ich suche nach konkreten Beispielen für diesen Fehler in realer Software.
Reprogrammierer
47

Double-Checked Locking. Im Großen und Ganzen.

Das Paradigma, mit dem ich bei meiner Arbeit bei BEA angefangen habe, die Probleme zu lernen, ist, dass die Leute einen Singleton folgendermaßen überprüfen:

public Class MySingleton {
  private static MySingleton s_instance;
  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) { s_instance = new MySingleton(); }
    }
    return s_instance;
  }
}

Dies funktioniert nie, da möglicherweise ein anderer Thread in den synchronisierten Block gelangt ist und s_instance nicht mehr null ist. Die natürliche Veränderung besteht also darin, es zu schaffen:

  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) {
        if(s_instance == null) s_instance = new MySingleton();
      }
    }
    return s_instance;
  }

Das funktioniert auch nicht, weil das Java-Speichermodell es nicht unterstützt. Sie müssen s_instance als flüchtig deklarieren, damit es funktioniert, und selbst dann funktioniert es nur unter Java 5.

Leute, die mit den Feinheiten des Java-Speichermodells nicht vertraut sind, bringen dies ständig durcheinander .

Kirk Wylie
quelle
7
Das Enum-Singleton-Muster löst all diese Probleme (siehe Josh Blochs Kommentare dazu). Das Wissen über seine Existenz sollte unter Java-Programmierern weiter verbreitet sein.
Robin
Ich habe noch keinen einzigen Fall gefunden, in dem eine verzögerte Initialisierung eines Singletons tatsächlich angebracht war. Und wenn ja, deklarieren Sie einfach die Methode synchronisiert.
Dov Wasserman
3
Dies ist, was ich für die verzögerte Initialisierung von Singleton-Klassen verwende. Es ist auch keine Synchronisation erforderlich, da dies implizit durch Java garantiert wird. Klasse Foo {statischer Klasseninhaber {statischer Foo foo = neuer Foo (); } static Foo getInstance () {return Holder.foo; }}
Irfan Zulfiqar
Irfan, das heißt Pughs Methode, soweit ich mich erinnere
Chris R
@Robin, ist es nicht einfacher, nur einen statischen Initialisierer zu verwenden? Diese laufen garantiert immer synchron.
Matt B
47

Nicht ordnungsgemäße Synchronisierung von Objekten, die von zurückgegeben werden Collections.synchronizedXXX(), insbesondere während der Iteration oder mehrerer Operationen:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

...

if(!map.containsKey("foo"))
    map.put("foo", "bar");

Das ist falsch . Trotz einzelner Operationen kann der synchronizedStatus der Zuordnung zwischen dem Aufrufen containsund putdurch einen anderen Thread geändert werden. Es sollte sein:

synchronized(map) {
    if(!map.containsKey("foo"))
        map.put("foo", "bar");
}

Oder mit einer ConcurrentMapImplementierung:

map.putIfAbsent("foo", "bar");
Dave Ray
quelle
5
Oder besser, verwenden Sie eine ConcurrentHashMap und putIfAbsent.
Tom Hawtin - Tackline
37

Obwohl dies wahrscheinlich nicht genau das ist, wonach Sie fragen, ist das häufigste Problem im Zusammenhang mit der Parallelität, auf das ich gestoßen bin (wahrscheinlich, weil es in normalem Single-Thread-Code auftritt) a

java.util.ConcurrentModificationException

verursacht durch Dinge wie:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }
Fabian Steeg
quelle
Nein, das ist genau das, wonach ich suche. Vielen Dank!
Alex Miller
30

Es kann leicht vorstellbar sein, dass synchronisierte Sammlungen Ihnen mehr Schutz bieten als sie tatsächlich, und vergessen, die Sperre zwischen Anrufen zu halten. Ich habe diesen Fehler einige Male gesehen:

 List<String> l = Collections.synchronizedList(new ArrayList<String>());
 String[] s = l.toArray(new String[l.size()]);

In der zweiten Zeile oben sind die Methoden toArray()und size()beide für sich genommen threadsicher, aber die size()werden getrennt von der ausgewertet toArray(), und die Sperre in der Liste wird zwischen diesen beiden Aufrufen nicht gehalten.

Wenn Sie diesen Code mit einem anderen Thread ausführen, der gleichzeitig Elemente aus der Liste entfernt, erhalten Sie früher oder später eine neue String[]Rückgabe, die größer als erforderlich ist, um alle Elemente in der Liste aufzunehmen, und im Ende Nullwerte enthält. Es ist leicht zu glauben, dass dies eine atomare Operation ist, da die beiden Methodenaufrufe an die Liste in einer einzigen Codezeile erfolgen, dies jedoch nicht.

Nick
quelle
5
gutes Beispiel. Ich denke, ich würde dies allgemeiner als "Zusammensetzung der atomaren Operationen ist nicht atomar" bezeichnen. (Siehe flüchtiges Feld ++ für ein weiteres einfaches Beispiel)
Alex Miller
29

Der häufigste Fehler, bei dem ich arbeite, ist, dass Programmierer lange Vorgänge wie Serveraufrufe auf dem EDT ausführen, die GUI für einige Sekunden sperren und die App nicht mehr reagieren lässt.

Eric Burke
quelle
Eine dieser Antworten, von denen ich mir wünschte, ich könnte mehr als einen Punkt geben
Epaga
2
EDT = Event Dispatch Thread
mjj1409
28

Vergessen, in einer Schleife zu warten () (oder Condition.await ()) und zu überprüfen, ob die Wartebedingung tatsächlich erfüllt ist. Ohne dies stoßen Sie auf Fehler von falschen wait () - Weckvorgängen. Kanonische Verwendung sollte sein:

 synchronized (obj) {
     while (<condition does not hold>) {
         obj.wait();
     }
     // do stuff based on condition being true
 }
Alex Miller
quelle
26

Ein weiterer häufiger Fehler ist die schlechte Behandlung von Ausnahmen. Wenn ein Hintergrundthread eine Ausnahme auslöst und Sie sie nicht richtig behandeln, wird die Stapelverfolgung möglicherweise überhaupt nicht angezeigt. Oder Ihre Hintergrundaufgabe wird nicht mehr ausgeführt und nie wieder gestartet, weil Sie die Ausnahme nicht behandelt haben.

Eric Burke
quelle
Ja, und es gibt gute Werkzeuge, um dies jetzt mit Handlern zu handhaben.
Alex Miller
3
Könnten Sie Links zu Artikeln oder Referenzen veröffentlichen, die dies näher erläutern?
Abhijeet Kashnia
22

Bis ich eine Klasse bei Brian Goetz besucht habe, war mir nicht klar, dass die Nicht-Synchronisation gettereines privaten Feldes, das durch eine Synchronisation mutiert setterist, niemals garantiert den aktualisierten Wert zurückgibt. Nur wenn eine Variable sowohl beim Lesen als auch beim Schreiben durch einen synchronisierten Block geschützt ist, erhalten Sie die Garantie für den neuesten Wert der Variablen.

public class SomeClass{
    private Integer thing = 1;

    public synchronized void setThing(Integer thing)
        this.thing = thing;
    }

    /**
     * This may return 1 forever and ever no matter what is set
     * because the read is not synched
     */
    public Integer getThing(){
        return thing;  
    }
}
John Russell
quelle
5
In den späteren JVMs (1.5 und höher, glaube ich) wird die Verwendung von Volatile dies ebenfalls beheben.
James Schek
2
Nicht unbedingt. volatile gibt Ihnen den neuesten Wert, sodass die Rückgabe von 1 nicht für immer verhindert wird, bietet jedoch keine Sperrung. Es ist nah, aber nicht ganz dasselbe.
John Russell
1
@ JohnRussell Ich dachte, volatil garantiert eine Beziehung, die vorher passiert. ist das nicht "sperren"? "Ein Schreibvorgang in eine flüchtige Variable (§8.3.1.4) v wird mit allen nachfolgenden Lesevorgängen von v durch einen beliebigen Thread synchronisiert (wobei nachfolgend gemäß der Synchronisationsreihenfolge definiert wird)."
Shawn
15

Ich denke, Sie schreiben Single-Threaded-Code, verwenden jedoch veränderbare Statiken (einschließlich Singletons). Offensichtlich werden sie zwischen Threads geteilt. Dies passiert überraschend oft.

Tom Hawtin - Tackline
quelle
3
Ja, in der Tat! Mutable Statics unterbrechen die Thread-Begrenzung. Überraschenderweise habe ich weder in JCiP noch in CPJ etwas über diese Falle gefunden.
Julien Chastang
Ich würde hoffen, dass dies für Leute, die gleichzeitig programmieren, offensichtlich ist. Der globale Status sollte der erste Ort sein, an dem die Gewindesicherheit überprüft wird.
Gtrak
1
@ Gary Thing ist, sie denken nicht, dass sie gleichzeitig programmieren.
Tom Hawtin - Tackline
15

Beliebige Methodenaufrufe sollten nicht innerhalb synchronisierter Blöcke erfolgen.

Dave Ray hat dies in seiner ersten Antwort angesprochen, und tatsächlich bin ich auch auf einen Deadlock gestoßen, der auch mit dem Aufrufen von Methoden für Listener innerhalb einer synchronisierten Methode zu tun hat. Ich denke, die allgemeinere Lektion ist, dass Methodenaufrufe nicht "in die Wildnis" innerhalb eines synchronisierten Blocks erfolgen sollten - Sie haben keine Ahnung, ob der Aufruf lange dauern wird, zu einem Deadlock führt oder was auch immer.

In diesem Fall und normalerweise im Allgemeinen bestand die Lösung darin, den Umfang des synchronisierten Blocks zu reduzieren, um nur einen kritischen privaten Codeabschnitt zu schützen .

Da wir jetzt außerhalb eines synchronisierten Blocks auf die Sammlung von Listenern zugegriffen haben, haben wir sie in eine Copy-on-Write-Sammlung geändert. Oder wir hätten einfach eine defensive Kopie der Sammlung erstellen können. Der Punkt ist, dass es normalerweise Alternativen gibt, um sicher auf eine Sammlung unbekannter Objekte zuzugreifen.

Scott Bale
quelle
13

Der letzte Fehler im Zusammenhang mit der Parallelität, auf den ich gestoßen bin, war ein Objekt, das in seinem Konstruktor einen ExecutorService erstellt hat. Wenn jedoch nicht mehr auf das Objekt verwiesen wurde, wurde der ExecutorService nie heruntergefahren. So über einen Zeitraum von Wochen, Tausende von Threads durchgesickert, was schließlich zum Absturz des Systems führte. (Technisch gesehen stürzte es nicht ab, funktionierte aber nicht mehr richtig, während es weiter lief.)

Technisch gesehen ist dies vermutlich kein Parallelitätsproblem, sondern ein Problem im Zusammenhang mit der Verwendung der Bibliotheken java.util.concurrency.

Eddie
quelle
11

Eine unausgeglichene Synchronisation, insbesondere gegen Karten, scheint ein ziemlich häufiges Problem zu sein. Viele Leute glauben, dass das Synchronisieren von Puts auf eine Map (keine ConcurrentMap, sondern eine HashMap) und das Nicht-Synchronisieren von Pets ausreichend ist. Dies kann jedoch zu einer Endlosschleife beim erneuten Hash führen.

Das gleiche Problem (teilweise Synchronisation) kann jedoch überall dort auftreten, wo Sie den Status für Lese- und Schreibvorgänge freigegeben haben.

Alex Miller
quelle
11

Bei Servlets ist ein Parallelitätsproblem aufgetreten, wenn veränderbare Felder vorhanden sind, die von jeder Anforderung festgelegt werden. Es gibt jedoch nur eine Servlet-Instanz für alle Anforderungen, sodass dies in einer Einzelbenutzerumgebung einwandfrei funktioniert hat. Wenn jedoch mehr als ein Benutzer das Servlet angefordert hat, sind unvorhersehbare Ergebnisse aufgetreten.

public class MyServlet implements Servlet{
    private Object something;

    public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException{
        this.something = request.getAttribute("something");
        doSomething();
    }

    private void doSomething(){
        this.something ...
    }
}
Ludwig Wensauer
quelle
10

Nicht gerade ein Fehler, aber die schlimmste Sünde ist, eine Bibliothek bereitzustellen, die andere verwenden sollen, aber nicht anzugeben, welche Klassen / Methoden threadsicher sind und welche nur von einem einzelnen Thread usw. aufgerufen werden dürfen.

Mehr Menschen sollten die in Goetz 'Buch beschriebenen Parallelitätsanmerkungen (z. B. @ThreadSafe, @GuardedBy usw.) verwenden.

Neil Bartlett
quelle
9

Mein größtes Problem waren immer Deadlocks, insbesondere durch Hörer, die mit einem gehaltenen Schloss ausgelöst wurden. In diesen Fällen ist es sehr einfach, eine umgekehrte Verriegelung zwischen zwei Threads zu erreichen. In meinem Fall zwischen einer Simulation, die in einem Thread ausgeführt wird, und einer Visualisierung der Simulation, die im UI-Thread ausgeführt wird.

BEARBEITEN: Der zweite Teil wurde verschoben, um die Antwort zu trennen.

Dave Ray
quelle
Können Sie die letzte in eine separate Antwort aufteilen? Lassen Sie es uns 1 pro Post behalten. Das sind zwei wirklich gute.
Alex Miller
9

Das Starten eines Threads im Konstruktor einer Klasse ist problematisch. Wenn die Klasse erweitert ist, kann der Thread gestartet werden, bevor der Konstruktor der Unterklasse ausgeführt wird.

grauer
quelle
8

Veränderbare Klassen in gemeinsam genutzten Datenstrukturen

Thread1:
    Person p = new Person("John");
    sharedMap.put("Key", p);
    assert(p.getName().equals("John");  // sometimes passes, sometimes fails

Thread2:
    Person p = sharedMap.get("Key");
    p.setName("Alfonso");

In diesem Fall ist der Code weitaus komplexer als in diesem vereinfachten Beispiel. Das Replizieren, Finden und Beheben des Fehlers ist schwierig. Vielleicht könnte es vermieden werden, wenn wir bestimmte Klassen als unveränderlich und bestimmte Datenstrukturen als nur unveränderliche Objekte enthaltend markieren könnten.

Steve McLeod
quelle
8

Die Synchronisierung mit einem Zeichenfolgenliteral oder einer durch ein Zeichenfolgenliteral definierten Konstante ist (möglicherweise) ein Problem, da das Zeichenfolgenliteral interniert ist und von allen anderen Personen in der JVM mit demselben Zeichenfolgenliteral gemeinsam genutzt wird. Ich weiß, dass dieses Problem bei Anwendungsservern und anderen "Container" -Szenarien aufgetreten ist.

Beispiel:

private static final String SOMETHING = "foo";

synchronized(SOMETHING) {
   //
}

In diesem Fall verwendet jeder, der die Zeichenfolge "foo" zum Sperren verwendet, dieselbe Sperre.

Alex Miller
quelle
Möglicherweise ist es gesperrt. Das Problem ist, dass die Semantik von WHEN Strings interniert ist undefiniert (oder, IMNSHO, unterdefiniert). Eine Compiler-Zeitkonstante von "foo" wird interniert, "foo", das von einer Netzwerkschnittstelle eingeht, wird nur interniert, wenn Sie dies tun.
Kirk Wylie
Richtig, deshalb habe ich speziell eine Literal-String-Konstante verwendet, die garantiert intern ist.
Alex Miller
8

Ich glaube, dass das Hauptproblem bei Java in Zukunft die (fehlenden) Sichtbarkeitsgarantien für Konstruktoren sein wird. Zum Beispiel, wenn Sie die folgende Klasse erstellen

class MyClass {
    public int a = 1;
}

und dann lesen Sie einfach die MyClass-Eigenschaft a aus einem anderen Thread. MyClass.a kann je nach Implementierung und Stimmung der JavaVM entweder 0 oder 1 sein. Heute sind die Chancen, dass 'a' 1 ist, sehr hoch. Bei zukünftigen NUMA-Maschinen kann dies jedoch anders sein. Viele Menschen sind sich dessen nicht bewusst und glauben, dass sie sich während der Initialisierungsphase nicht um Multithreading kümmern müssen.

Tim Jansen
quelle
Ich finde das leicht überraschend, aber ich weiß, dass du ein kluger Kerl bist, Tim, also nehme ich es ohne Bezug. :) Wenn a jedoch endgültig wäre, wäre dies kein Problem, richtig? Sie wären dann während der Erstellung an die endgültige Einfriersemantik gebunden?
Alex Miller
Ich finde immer noch Dinge im JMM, die mich überraschen, also würde ich mir nicht vertrauen, aber ich bin mir ziemlich sicher. Siehe auch cs.umd.edu/~pugh/java/memoryModel/… . Wenn das Feld endgültig wäre, wäre es kein Problem, dann wäre es nach der Initialisierungsphase sichtbar.
Tim Jansen
2
Dies ist nur dann ein Problem, wenn die Referenz der frisch erstellten Instanz bereits verwendet wird, bevor der Konstruktor zurückgegeben / beendet wurde. Zum Beispiel registriert sich die Klasse während der Erstellung in einem öffentlichen Pool und andere Threads beginnen, darauf zuzugreifen.
ReneS
3
MyClass.a zeigt statischen Zugriff an und 'a' ist kein statisches Mitglied von MyClass. Abgesehen davon, wie 'ReneS' besagt, ist dies nur dann ein Problem, wenn ein Verweis auf das nicht abgeschlossene Objekt durchgesickert ist, wie zum Beispiel das Hinzufügen von 'this' zu einer externen Map im Konstruktor.
Markus Jevring
7

Der dümmste Fehler, den ich häufig mache, ist das Vergessen der Synchronisierung, bevor ich notify () oder wait () für ein Objekt aufrufe.

Dave Ray
quelle
8
Ist dieses Problem im Gegensatz zu den meisten Parallelitätsproblemen nicht leicht zu finden? Zumindest bekommen Sie hier eine IllegalMonitorStateException ...
Outlaw Programmer
Zum Glück ist es sehr leicht zu finden ... aber es ist immer noch ein dummer Fehler, der meine Zeit mehr verschwendet als es sollte :)
Dave Ray
7

Verwenden eines lokalen "neuen Objekts ()" als Mutex.

synchronized (new Object())
{
    System.out.println("sdfs");
}

Das ist nutzlos.

Ludwig Wensauer
quelle
2
Dies ist wahrscheinlich nutzlos, aber das Synchronisieren macht einige interessante Dinge ... Sicherlich ist es eine völlige Verschwendung, jedes Mal ein neues Objekt zu erstellen.
Baum
4
Es ist nicht nutzlos. Es ist eine Speicherbarriere ohne Schloss.
David Roussel
1
@ David: das einzige Problem - JVM könnte es optimieren, indem es eine solche Sperre überhaupt entfernt
anderer Codierer
@insighter Ich sehe Ihre Meinung geteilt wird ibm.com/developerworks/java/library/j-jtp10185/index.html stimme ich , dass es eine dumme Sache zu tun, wie Sie wissen nicht , wann Ihre memoery Barriere synchronisiert, I Ich habe nur darauf hingewiesen, dass es mehr als nichts tut.
David Roussel
7

Ein weiteres häufiges Problem der Parallelität ist die Verwendung von synchronisiertem Code, wenn dieser überhaupt nicht erforderlich ist. Zum Beispiel sehe ich immer noch Programmierer, die StringBufferoder sogar java.util.Vector(als lokale Methodenvariablen) verwenden.

Kutzi
quelle
1
Dies ist kein Problem, aber unnötig, da die JVM die Daten mit dem globalen Speicher synchronisiert und daher möglicherweise auf mehreren CPUs schlecht ausgeführt wird, obwohl niemand den Synchronisationsblock gleichzeitig verwendet.
ReneS
6

Mehrere Objekte, die gesperrt sind, auf die jedoch häufig nacheinander zugegriffen wird. Wir sind auf einige Fälle gestoßen, in denen die Sperren durch unterschiedlichen Code in unterschiedlicher Reihenfolge erhalten werden, was zu einem Deadlock führt.

Brendan Cashman
quelle
5

Nicht zu erkennen, dass das thisin einer inneren Klasse nicht das thisder äußeren Klasse ist. Normalerweise in einer anonymen inneren Klasse, die implementiert Runnable. Das Grundproblem ist, dass die Synchronisation Teil von allem istObject es keine statische Typprüfung s ist. Ich habe dies mindestens zweimal im Usenet gesehen und es erscheint auch in Brian Goetz'z Java Concurrency in Practice.

BGGA-Verschlüsse leiden nicht darunter, da es keine thisfür den Verschluss gibt ( thisverweist auf die äußere Klasse). Wenn Sie Nicht- thisObjekte als Sperren verwenden, wird dieses und andere Probleme umgangen.

Tom Hawtin - Tackline
quelle
3

Verwendung eines globalen Objekts wie einer statischen Variablen zum Sperren.

Dies führt aufgrund von Konflikten zu einer sehr schlechten Leistung.

kohlerm
quelle
Manchmal manchmal nicht. Wenn es nur so einfach wäre ...
gimpf
Unter der Annahme, dass Threading überhaupt dazu beiträgt, die Leistung für das angegebene Problem zu steigern, wird die Leistung immer beeinträchtigt, sobald mehr als ein Thread auf Code zugreift, der durch die Sperre geschützt ist.
Kohlerm
3

Honesly? Vor dem Aufkommen von java.util.concurrentwar das häufigste Problem, auf das ich routinemäßig gestoßen bin, das, was ich als "Thread-Thrashing" bezeichne: Anwendungen, die Threads für die Parallelität verwenden, aber zu viele davon erzeugen und am Ende Thrashing verursachen.

Brian Clapper
quelle
Wollen Sie damit sagen, dass Sie jetzt, da java.util.concurrent verfügbar ist, auf weitere Probleme stoßen?
Andrzej Doyle