it-swarm.com.de

Was ist das häufigste Nebenläufigkeitsproblem in Java?

Hierbei handelt es sich um eine Art Umfrage zu häufigen Parallelitätsproblemen in Java. Ein Beispiel könnte die klassische Deadlock- oder Racebedingung oder EDT-Threading-Fehler in Swing sein. Ich interessiere mich sowohl für eine Vielzahl möglicher Probleme als auch für die häufigsten Probleme. Bitte hinterlassen Sie eine spezifische Antwort auf den Fehler Java Nebenläufigkeit) pro Kommentar und stimmen Sie ab, wenn Sie eine Antwort sehen, auf die Sie gestoßen sind.

188
Alex Miller

Das häufigste Nebenläufigkeitsproblem, das ich gesehen habe, besteht darin, nicht zu erkennen, dass ein von einem Thread geschriebenes Feld nicht garantiert ist, um von einem anderen Thread gesehen zu werden. 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 stop nicht flüchtig oder setStop und run nicht synchronisiert ist, funktioniert dies nicht garantiert. Dieser Fehler ist besonders teuflisch, da er in der Praxis zu 99,999% keine Rolle spielt, da der Leser-Thread die Änderung irgendwann sehen wird - aber wir wissen nicht, wann er sie gesehen hat.

123
Kutzi

Mein # 1 schmerzhaftestes Nebenläufigkeitsproblem 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, ist der Literal-String "LOCK" entpuppt sich als dieselbe Instanz von Java.lang.String (obwohl sie völlig unterschiedlich deklariert sind.) Das Ergebnis ist offensichtlich schlecht.

173
Jared

Ein klassisches Problem ist das Ändern des Objekts, auf dem Sie synchronisieren, während Sie es synchronisieren:

synchronized(foo) {
  foo = ...
}

Andere gleichzeitige Threads werden dann auf einem anderen Objekt synchronisiert, und dieser Block bietet nicht den erwarteten gegenseitigen Ausschluss.

64
Alex Miller

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 Synchronisation. Diese Klassen sind nicht threadsicher, sodass der Zugriff mit mehreren Threads letztendlich zu seltsamen Problemen mit inkonsistenten Zuständen führt.

49
Alex Miller

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

Das Paradigma, das ich während meiner Arbeit bei BEA kennengelernt habe, 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 Änderung ist also, es zu machen:

  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 Memory Model es nicht unterstützt. Sie müssen s_instance als volatile deklarieren, damit es funktioniert, und selbst dann funktioniert es nur auf Java 5.

Leute, die nicht mit den Feinheiten des Java Memory Model vertraut sind, machen das kaputt die ganze Zeit.

46
Kirk Wylie

Nicht richtig Synchronisierung für Objekte, die von Collections.synchronizedXXX() zurückgegeben wurden, 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. Obwohl es sich bei den einzelnen Operationen um synchronized handelt, kann der Status der Zuordnung zwischen dem Aufrufen von contains und put von einem anderen Thread geändert werden. Es sollte sein:

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

Oder mit einer ConcurrentMap Implementierung:

map.putIfAbsent("foo", "bar");
45
Dave Ray

Wahrscheinlich nicht genau das, wonach Sie fragen, aber 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), ist 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); }
36
Fabian Steeg

Der häufigste Fehler, den ich sehe, 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.

28
Eric Burke

Man kann sich leicht vorstellen, dass synchronisierte Sammlungen Ihnen mehr Schutz bieten als sie tatsächlich tun, und vergessen, die Sperre zwischen Anrufen beizubehalten. Ich habe diesen Fehler ein paar Mal gesehen:

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

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

Wenn Sie diesen Code mit einem anderen Thread ausführen gleichzeitig Elemente aus der Liste entfernen, wird früher oder später ein neuer String[] Zurückgegeben, der größer ist als erforderlich, um alle Elemente in der Liste aufzunehmen die Liste und hat Nullwerte im Schwanz. Man kann sich leicht vorstellen, dass dies eine atomare Operation ist, da die beiden Methodenaufrufe für die List in einer einzigen Codezeile vorkommen, dies jedoch nicht.

28
Nick

Vergessen, in einer Schleife zu warten () (oder Condition.await ()) und zu prüfen, ob die Wartebedingung tatsächlich wahr ist. Ohne dies stoßen Sie auf Fehler, die durch falsche wait () - Aufweckvorgänge verursacht wurden. Kanonische Verwendung sollte sein:

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

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

25
Eric Burke

Bis ich einen Kurs bei Brian Goetz belegte, wusste ich nicht, dass das nicht synchronisierte getter eines privaten Feldes, das durch ein synchronisiertes setter mutiert wurde, niemals ist garantiert den aktualisierten Wert zurückzugeben. Nur wenn eine Variable durch einen synchronisierten Block am sowohl Lesen als auch Schreiben 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;  
    }
}
21
John Russell

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

14

Beliebige Methodenaufrufe sollten nicht aus synchronisierten Blöcken erfolgen.

Dave Ray hat dies in seiner ersten Antwort angesprochen, und tatsächlich bin ich auch auf einen Deadlock gestoßen, der damit zu tun hat, Methoden von Listenern innerhalb einer synchronisierten Methode aufzurufen. Ich denke, die allgemeinere Lektion ist, dass Methodenaufrufe nicht aus einem synchronisierten Block heraus "in die Wildnis" gemacht werden sollten - Sie haben keine Ahnung, ob der Aufruf lange dauert, zu einem Deadlock führt oder was auch immer.

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

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

13
Scott Bale

Der letzte Concurrency-bezogene Fehler, 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. Daher sind über einen Zeitraum von Wochen Tausende 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.)

Ich nehme an, dass dies technisch gesehen kein Nebenläufigkeitsproblem ist, sondern ein Problem im Zusammenhang mit der Verwendung der Java.util.concurrency-Bibliotheken.

12
Eddie

Unausgeglichene Synchronisation, insbesondere gegen Maps, scheint ein recht verbreitetes Problem zu sein. Viele Leute glauben, dass das Synchronisieren von Puts auf einer Karte (nicht einer ConcurrentMap, sondern einer HashMap) und das Nicht-Synchronisieren von Gets ausreichend ist. Dies kann jedoch zu einer Endlosschleife beim erneuten Hashing führen.

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

10
Alex Miller

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 Umgebung mit nur einem Benutzer einwandfrei funktioniert hat. Wenn jedoch mehr als ein Benutzer das Servlet anforderte, traten unvorhersehbare Ergebnisse auf.

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 ...
    }
}
10
Ludwig Wensauer

Nicht gerade ein Fehler, aber die schlimmste Sünde ist die Bereitstellung einer Bibliothek, die Sie anderen Benutzern zur Verfügung stellen möchten, ohne jedoch anzugeben, welche Klassen/Methoden threadsicher sind und welche nur von einem einzigen Thread aus aufgerufen werden dürfen usw.

Weitere Personen sollten die in Goetz 'Buch beschriebenen Annotationen für die gleichzeitige Verwendung (z. B. @ThreadSafe, @GuardedBy usw.) verwenden.

10
Neil Bartlett

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

9
grayger

Mein größtes Problem waren schon immer Deadlocks, insbesondere durch Zuhörer, die mit gehaltener Sperre gefeuert werden. In diesen Fällen ist es sehr einfach, ein invertiertes Sperren zwischen zwei Threads zu erhalten. 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: Zweiter Teil verschoben, um Antwort zu trennen.

9
Dave Ray

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. Es ist schwierig, den Fehler zu replizieren, zu finden und zu beheben. Vielleicht könnte es vermieden werden, wenn wir bestimmte Klassen als unveränderlich und bestimmte Datenstrukturen als nur unveränderliche Objekte kennzeichnen könnten.

8
Steve McLeod

Ich glaube in der Zukunft wird das Hauptproblem bei Java) die (fehlende) Sichtbarkeitsgarantie für Konstruktoren sein, zum Beispiel, wenn Sie die folgende Klasse erstellen

class MyClass {
    public int a = 1;
}

und dann lese einfach die MyClass-Eigenschaft a aus einem anderen Thread, MyClass.a könnte entweder 0 oder 1 sein, abhängig von der JavaVM-Implementierung und der Stimmung. Heute sind die Chancen sehr hoch, dass 'a' 1 ist. 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.

8
Tim Jansen

Die Synchronisierung mit einem Zeichenfolgenliteral oder einer Konstante, die durch ein Zeichenfolgenliteral definiert ist, ist (möglicherweise) ein Problem, da das Zeichenfolgenliteral interniert ist und von allen anderen Benutzern in der JVM mit demselben Zeichenfolgenliteral gemeinsam verwendet 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 teilt sich jeder, der die Zeichenfolge "foo" zum Sperren verwendet, die gleiche Sperre.

8
Alex Miller

Ein weiteres häufig auftretendes Problem ist die Verwendung von synchronisiertem Code, wenn dies überhaupt nicht erforderlich ist. Zum Beispiel sehe ich immer noch Programmierer, die StringBuffer oder sogar Java.util.Vector (Als lokale Methodenvariablen) verwenden.

7
Kutzi

Der dümmste Fehler, den ich häufig mache, besteht darin, die Synchronisierung vor dem Aufruf von notify () oder wait () für ein Objekt zu vergessen.

7
Dave Ray

Verwendung eines lokalen "new Object ()" als Mutex.

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

Das ist sinnlos.

7
Ludwig Wensauer

Mehrere Objekte, die durch Sperren geschützt sind, auf die jedoch normalerweise nacheinander zugegriffen wird. In einigen Fällen werden die Sperren durch unterschiedlichen Code in unterschiedlicher Reihenfolge abgerufen, was zu einem Deadlock führt.

6
Brendan Cashman

Nicht zu erkennen, dass das this in einer inneren Klasse nicht das this der äußeren Klasse ist. Normalerweise in einer anonymen inneren Klasse, die Runnable implementiert. Das Hauptproblem besteht darin, dass die Synchronisation Teil aller Objects ist und somit keine statische Typprüfung stattfindet. Ich habe dies mindestens zweimal im Usenet gesehen und es erscheint auch in Brian Goetz'z Java Concurrency in Practice.

BGGA-Closures leiden nicht darunter, da es kein this für den Closure gibt (this verweist auf die äußere Klasse). Wenn Sie nicht -this -Objekte als Sperren verwenden, können Sie dieses und andere Probleme umgehen.

Nicht realisiert das Java.awt.EventQueue.invokeAndWaitverhält sich wie eine Sperre (exklusiver Zugriff auf den Event Dispatch Thread, EDT). Das Tolle an Deadlocks ist, dass Sie selbst dann, wenn dies selten vorkommt, einen Stack-Trace mit jstack oder ähnlichem erstellen können. Ich habe dies in einer Reihe von weit verbreiteten Programmen gesehen (ein Fix für ein Problem, das ich nur einmal in Netbeans gesehen habe, sollte in der nächsten Version enthalten sein).

1) Ein häufiger Fehler, auf den ich gestoßen bin, besteht darin, eine synchronisierte Collection-Klasse zu durchlaufen. Es ist erforderlich, manuell zu synchronisieren, bevor und während der Iteration der Iterator abgerufen wird.

2) Ein weiterer Fehler ist, dass die meisten Lehrbücher den Eindruck erwecken, dass das Sichern eines Klassen-Threads nur eine Frage des Hinzufügens synchronisierter Threads bei jeder Methode ist. Das ist an sich keine Garantie - es schützt nur die Integrität der jeweiligen Klasse, aber die Ergebnisse können immer noch undeterministisch sein.

3) Wenn zu viele zeitaufwendige Operationen in einen synchronisierten Block eingefügt werden, führt dies häufig zu einer sehr schlechten Leistung. Glücklicherweise kann das Future-Muster im Concurrency-Paket den Tag retten.

4) Das Zwischenspeichern von veränderlichen Objekten zur Verbesserung der Leistung führt häufig auch zu Multithreading-Problemen (und ist manchmal sehr schwer zu verfolgen, da Sie davon ausgehen, dass Sie der einzige Benutzer sind).

5) Die Verwendung mehrerer Synchronisationsobjekte muss sorgfältig behandelt werden.

3
David Nouls

Versuchen Sie diesen Code ..

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 ...
    }
}
3
Uxgru Revisions

Verwendung eines globalen Objekts wie einer statischen Variablen zum Sperren.

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

3
kohlerm

Aufrichtig? Vor dem Aufkommen von Java.util.concurrent, das häufigste Problem, auf das ich routinemäßig gestoßen bin, war das, was ich als "Thread-Thrashing" bezeichne: Anwendungen, die Threads für den gemeinsamen Zugriff verwenden, aber zu viele von ihnen erzeugen und am Ende Thrashing verursachen.

3
Brian Clapper

Wenn Sie Java RMI starten, wird eine Hintergrund-Task ausgeführt, die den Garbage Collector dazu zwingt, alle 60 Sekunden ausgeführt zu werden. Dies kann an sich eine gute Sache sein, der RMI-Server jedoch möglicherweise nicht Wird direkt von Ihnen gestartet, aber von einem Framework/Tool, das Sie verwenden (z. B. JRun) .Und das RMI wird möglicherweise nicht für irgendetwas verwendet.

Das Nettoergebnis ist ein System.gc () -Aufruf einmal pro Minute. Auf einem stark ausgelasteten System wird in Ihren Protokollen die folgende Ausgabe angezeigt: 60 Sekunden Aktivität, gefolgt von einer langen Gc-Pause, gefolgt von 60 Sekunden Aktivität, gefolgt von einer langen Gc-Pause. Dies ist fatal für den Durchsatz.

Die Lösung besteht darin, explizite gc mit -XX zu deaktivieren: + DisableExplicitGC

3
JodaStephen

Fehler beim Bereitstellen klar definierter Lebenszyklusmethoden für Objekte, die lang laufende Threads verwalten. Ich mag es, Paare von Methoden namens init () und destroy () zu erstellen. Es ist auch wichtig, destroy () aufzurufen, damit Ihre App ordnungsgemäß beendet werden kann.

2
Eric Burke

Eine Methode, die Daten in einer Instanzvariablen speichert, um "Aufwand zu sparen", indem sie an Hilfsmethoden übergeben wird, wenn eine andere Methode, die gleichzeitig aufgerufen werden kann, dieselben Instanzvariablen für ihre eigenen Zwecke verwendet.

Die Daten sollten stattdessen als Methodenparameter für die Dauer des synchronisierten Aufrufs weitergegeben werden. Dies ist nur eine kleine Vereinfachung meines schlechtesten Gedächtnisses:

public class UserService {

    private String userName;

    public String getUserName() {
        return userName;
    }

    public void login(String name) {
        this.userName = name; 
        doLogin();
    }

    private void doLogin() {
        userDao.login(getUserName());
    }

    public void delete(String name) {
        this.userName = name; 
        doDelete();
    }

    private void doDelete() {
        userDao.delete(getUserName());
    }

}

Die Anmelde- und Abmeldemethoden müssen logischerweise nicht synchronisiert werden. In der vorliegenden Form können Sie jedoch alle Arten von unterhaltsamen Kundendienstanrufen erleben.

2
Dov Wasserman

Parallelitätsproblem bei der Verwendung verschiedener Sperrobjekte mit Wait and Notify.

Ich habe versucht, wait () und notifyAll () zu benutzen und hier ist, wie ich es benutzt habe und in die Hölle gefallen bin.

Thread1

Object o1 = new Object();

synchronized(o1) {
    o1.wait();
}

Und in anderem Thread. Thread - 2

Object o2 = new Object();

synchronized(o2) {
    o2.notifyAll();
}

Thread1 wartet auf o1 und Thread2, der o1.notifyAll () hätte aufrufen sollen, ruft o2.notifyAll () auf. Thread 1 wird niemals aufwachen.

Und natürlich das allgemeine Problem, dass wait () oder notifyAll () in synchronisierten Blöcken nicht aufgerufen und nicht mit demselben Objekt aufgerufen werden, mit dem der Block synchronisiert wird.

Object o2 = new Object();

synchronized(o2) {
    notifyAll();
}

Dies führt zu einer IllegalMonitorStateException, da der Thread, der notifyAll () aufgerufen hat, notifyAll () mit diesem Objekt aufgerufen hat, aber nicht der Eigentümer des Sperrobjekts ist. Aber der aktuelle Thread ist der Besitzer des O2-Sperrobjekts.

2
Deepak Jain

Halten Sie alle Threads beschäftigt.

Dies ist am häufigsten der Fall, wenn Probleme im Code anderer behoben werden müssen, da diese die Sperrkonstrukte missbraucht haben. In letzter Zeit haben meine Kollegen anscheinend festgestellt, dass es Spaß macht, Leser-/Schreiberschlösser zu verwenden, während ein kleiner Gedanke ihre Bedürfnisse gänzlich beseitigt.

In meinem eigenen Code ist es weniger offensichtlich, aber herausfordernd, die Threads zu beschäftigen. Es erfordert tiefere Überlegungen zu Algorithmen, z. B. das Schreiben neuer Datenstrukturen oder das sorgfältige Entwerfen eines Systems, um sicherzustellen, dass bei der Verwendung von Sperren keine Konflikte auftreten.

Das Lösen von Fehlern bei der gleichzeitigen Verwendung ist einfach - es kann schwierig sein, herauszufinden, wie Sperrenkonflikte vermieden werden können.

2
Ben Manes

Rennbedingungen während der Finalize/Release/Shutdown/Destructor-Methode eines Objekts und bei normalen Aufrufen.

Von Java aus integriere ich viel in Ressourcen, die geschlossen werden müssen, wie z. B. COM-Objekte oder Flash Player. Entwickler vergessen immer, dies richtig zu machen, und es kommt vor, dass ein Thread ein heruntergefahrenes Objekt aufruft.

2
Jen S.

Seit Java 5 gibt es Thread.getUncaughtExceptionHandler, aber dieser UncaughtExceptionHandler wird niemals aufgerufen, wenn ein ExecutorService/ThreadPool verwendet wird.
Zumindest konnte ich den UncaughtExceptionHandler mit einem ExcutorService nicht zum Laufen bringen.

1
Ludwig Wensauer

Aktualisieren einer Swing-UI-Komponente (normalerweise ein Fortschrittsbalken) in einem Worker-Thread anstelle des Swing-Threads (man sollte natürlich SwingUtilities.invokeLater(Runnable) verwenden, aber wenn Sie dies vergessen, kann der Fehler lange dauern zur Oberfläche.)

1
finnw

Ich bin auf einen Pseudo-Deadlock von einem E/A-Thread gestoßen, der ein Countdown-Latch erstellt hat. Eine stark vereinfachte Version des Problems sieht folgendermaßen aus:

 öffentliche Klasse MyReader implementiert Runnable {
 
 private final CountDownLatch done = new CountDownLatch (1); 
 private volatile isOkToRun = true; 
 
 public void run () {
 while (isOkToRun) {
 sendMessage (getMessaage ()); 
} 
 done.countDown (); 
} 
 
 public void stop () {
 isOkToRun = false; 
 done.await (); 
} 
 
} 

Die Idee von stop () ist, dass es nicht zurückgegeben wurde, bis der Thread beendet wurde. Bei der Rückkehr befand sich das System in einem bekannten Zustand. Dies ist in Ordnung, es sei denn, sendMessage () ruft stop () auf und wartet dort für immer. Solange stop () nicht vom Runnable aufgerufen wird, funktioniert alles wie erwartet. In einer großen Anwendung ist die Aktivität des Runnable-Threads jedoch möglicherweise nicht offensichtlich!

Die Lösung bestand darin, wait () mit einer Zeitüberschreitung von einigen Sekunden aufzurufen und einen Stapelspeicherauszug und eine Beschwerde zu protokollieren, sobald die Zeitüberschreitung auftrat. Dies bewahrte das gewünschte Verhalten, wenn es möglich war, und deckte Codierungsprobleme auf, wenn sie auftraten.

1
Eddie

Meine zwei Cent für den Versuch, Synchronisierungsprobleme von Anfang an zu vermeiden - achten Sie auf die folgenden Probleme/Gerüche:

  1. Wenn Sie Code schreiben, müssen Sie immer wissen, in welchem ​​Thread Sie sich befinden.
  2. Wenn Sie eine Klasse oder API zur Wiederverwendung entwerfen, sollten Sie immer fragen Sie sich, ob der Code thread-sicher sein muss. Es ist besser, eine bewusste Entscheidung zu treffen und zu dokumentieren, dass Ihre Einheit nicht threadsicher ist, als eine unkluge Synchronisierung mit potenziellem Deadlock vorzunehmen.
  3. Aufrufe von new Thread() sind ein Geruch. Verwenden Sie stattdessen dedizierte ExecutorServices, die Sie dazu zwingen, über das allgemeine Threading-Konzept Ihrer Anwendung nachzudenken (siehe 1) und andere dazu zu ermutigen, diesem zu folgen.
  4. Bibliotheksklassen kennen und verwenden (wie AtomicBoolean ua , synchronisierte Sammlungen usw.). Nochmals: Treffen Sie eine bewusste Entscheidung darüber, ob Thread-Sicherheit in einem bestimmten Kontext wichtig ist, und verwenden Sie sie nicht einfach blind.
1
Rahel Lüthy

Das größte Problem, auf das ich gestoßen bin, sind Entwickler, die nachträglich Multithreading-Unterstützung hinzufügen.

1
Javamann
public class ThreadA implements Runnable {
    private volatile SharedObject obj;

    public void run() {
        while (true) {
            obj = new SharedObject();
            obj.setValue("Hallo");
        }
    }

    public SharedObject getObj() {
        return obj;
    }
}

Das Problem, auf das ich hier (unter anderem) hinweisen möchte, ist, dass das Löschen des SharedObject-Objekts erfolgt, bevor der Wert "Hallo" festgelegt wird. Dies bedeutet, dass der Konsument von getObj () möglicherweise eine Instanz abruft, in der getValue () null zurückgibt.

public class ThreadB implements Runnable {
    ThreadA a = null;

    public ThreadB(ThreadA a) {
        this.a = a;
    }

    public void run() {
        while (true) {
            try {
                System.out.println("SharedObject: " + a.getObj().getVal());
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class SharedObject {
    private String val = null;

    public SharedObject() {
    }

    public String getVal() {
        return val;
    }

    public void setVal(String val) {
        this.val = val;
    }
}
0
dlinsin

Ein böses Problem, das ich in Java) gefunden habe, besteht darin, dass mehrere Threads ohne Synchronisation auf eine HashMap zugreifen. Wenn einer liest und einer schreibt, besteht eine gute Chance, dass der Leser unendlich wird loop (die Bucket-Node-Listenstruktur wird in eine Loop-Liste umgewandelt).

Natürlich sollten Sie dies nicht in erster Linie tun (verwenden Sie ConcurrentHashMap oder Collections.synch ... wrapper), aber es scheint dasjenige zu sein, das immer durch das Netz geht und einen ordnungsgemäßen Fadenstau verursacht, das System ist völlig kaputt, normalerweise bedingt zu einer Utility-Klasse, die eine solche Karte enthält, die ein paar Ebenen weiter im Stapel liegt und an die niemand denkt.

0
DaveC

Unterstützung bei der Implementierung von Actors in Functional Java und Benchmarking von Millionen von Threads auf Multi-Core-Rechnern.

0
Tony Morris

Ich denke, das häufigste Nebenläufigkeitsproblem in Java) ist Code, der bisher anscheinend funktioniert, obwohl er überhaupt nicht threadsicher ist. Dank eines winzigen Fehlers wird er zu einer Zeitbombe und in fast allen Fällen wissen Sie das nicht im Voraus, weil es für Sie nicht offensichtlich ist. Während normaler fehlerhafter Code hoffentlich während der Tests fehlschlägt, schlägt gleichzeitiger Code häufig nur letztendlich und nicht reproduzierbar fehl.

0
b_erb
while(true)
{
   if (...)
     break

   doStuff()
}

Wenn Entwickler while-Schleifen schreiben, verpassen sie ausnahmslos das "Ressourcen-Commit" in ihrem eigenen Code.

Nämlich, wenn dieser Block nicht beendet wird, stürzt die Anwendung und möglicherweise sogar das System ab und stirbt ab. Nur wegen einer einfachen while(fantasy_land)...if(...) break.

0
Bob