4 Stimmen

Wie gut ist die JVM in der Parallelverarbeitung? Wann sollte ich meine eigenen Threads und Runnables erstellen? Warum können sich Threads gegenseitig stören?

Ich habe ein Java-Programm, das viele kleine Simulationen durchführt. Es führt einen genetischen Algorithmus aus, bei dem jede Fitnessfunktion eine Simulation mit Parametern für jedes Chromosom ist. Jede Simulation dauert vielleicht 10 oder so Sekunden, wenn sie alleine läuft, und ich möchte eine ziemlich große Populationsgröße (sagen wir 100?) ausführen. Ich kann die nächste Simulationsrunde erst starten, wenn die vorherige beendet ist. Ich habe Zugang zu einem Rechner mit einer ganzen Reihe von Prozessoren und frage mich, ob ich etwas tun muss, damit die Simulationen parallel laufen. Ich habe noch nie etwas explizit für Multicore-Prozessoren geschrieben, und ich weiß, dass das eine gewaltige Aufgabe ist.

Ich würde also gerne Folgendes wissen: In welchem Umfang und wie gut parallelisiert die JVM? Ich habe gelesen, dass sie Low-Level-Threads erstellt, aber wie intelligent ist sie? Wie effizient ist sie? Würde mein Programm schneller laufen, wenn ich jede Simulation zu einem Thread machen würde? Ich weiß, dass dies ein umfangreiches Thema ist, aber könnten Sie mich auf einführende Literatur über Parallelverarbeitung und Java hinweisen?

Herzlichen Dank!

Aktualisierung: Ok, ich habe einen ExecutorService implementiert und meine kleinen Simulationen implementieren Runnable und haben run() Methoden. Anstatt dies zu schreiben:

Simulator sim = new Simulator(args); 
sim.play(); 
return sim.getResults(); 

Ich schreibe dies in meinen Konstruktor:

ExecutorService executor = Executors.newFixedThreadPool(32);

Und jedes Mal, wenn ich dem Pool eine neue Simulation hinzufügen möchte, führe ich dies aus:

RunnableSimulator rsim = new RunnableSimulator(args); 
exectuor.exectue(rsim); 
return rsim.getResults(); 

Le site RunnableSimulator::run() Methode ruft die Simulator::play() Methode, beide haben keine Argumente.

Ich glaube, ich habe eine Fadenstörung, weil die Simulationen jetzt fehlschlagen. Mit "Fehler" meine ich, dass Variablen Werte halten, die sie eigentlich nicht halten sollten. Es wurde kein Code innerhalb der Simulation geändert, und vorher lief die Simulation perfekt über viele, viele verschiedene Argumente. Die Simulation funktioniert folgendermaßen: In jeder Runde erhält sie ein Spielteil und durchläuft alle Orte auf dem Spielbrett. Es wird geprüft, ob der angegebene Ort gültig ist, und wenn ja, wird die Spielfigur übertragen und die Güte des Brettes gemessen. Offensichtlich werden nun ungültige Orte an die Commit-Methode übergeben, was dazu führt, dass der Index überall außerhalb der Grenzen liegt.

Jede Simulation ist ein eigenes Objekt, richtig? Basierend auf dem obigen Code? Ich kann genau denselben Satz von Argumenten an die RunnableSimulator y Simulator Klassen und der lauffähigen Version werden Ausnahmen ausgelöst. Was denken Sie, könnte die Ursache dafür sein und was kann ich tun, um dies zu verhindern? Kann ich einige Code-Beispiele in einer neuen Frage zur Verfügung stellen, um zu helfen?

11voto

Adam Jaskiewicz Punkte 10844

Java-Tutorial zur Gleichzeitigkeit

Wenn Sie nur einen Haufen Sachen in verschiedene Threads auslagern und nicht zwischen verschiedenen Threads hin und her sprechen wollen, ist das nicht allzu schwer; schreiben Sie einfach jeden in eine Lauffähig und geben sie an einen ExecutorService .

Sie sollten den gesamten Leitfaden überfliegen, aber für diese spezielle Aufgabe, hier beginnen .

Im Grunde geht man folgendermaßen vor:

ExecutorService executorService = Executors.newFixedThreadPool(n);

wobei n die Anzahl der Dinge ist, die gleichzeitig laufen sollen (normalerweise die Anzahl der CPUs). Jede Ihrer Aufgaben sollte ein Objekt sein, das Runnable implementiert, und Sie führen es dann auf Ihrem ExecutorService aus:

executorService.execute(new SimulationTask(parameters...));

Executors.newFixedThreadPool(n) wird in Betrieb genommen n Threads, und die Ausführung fügt die Aufgaben in eine Warteschlange ein, die diesen Threads zugeführt wird. Wenn eine Aufgabe beendet ist, ist der Thread, auf dem sie lief, nicht mehr beschäftigt, und die nächste Aufgabe in der Warteschlange beginnt auf ihm zu laufen. Execute blockiert nicht, sondern stellt die Aufgabe einfach in die Warteschlange und geht zur nächsten über.

Die Sache, auf die man achten muss, ist, dass man wirklich KEINEN veränderbaren Zustand zwischen den Aufgaben teilt. Ihre Aufgabenklassen sollten nicht von irgendetwas abhängen, das veränderbar ist und von ihnen gemeinsam genutzt wird (d.h. statische Daten). Es gibt Möglichkeiten, mit gemeinsam genutzten veränderlichen Zuständen umzugehen (Sperren), aber wenn Sie das Problem vollständig vermeiden können, wird es viel einfacher sein.

EDIT: Lesen Sie Ihre Bearbeitungen zu Ihrer Frage, es sieht aus wie Sie wirklich etwas ein wenig anders wollen. Anstatt zu implementieren Runnable umsetzen. Callable . Ihr call() Methode sollte so ziemlich die gleiche sein wie Ihre derzeitige run() außer es sollte return getResults(); . Dann, submit() es zu Ihrem ExecutorService . Sie erhalten eine Future die Sie verwenden können, um zu prüfen, ob die Simulation durchgeführt wurde, und um Ihre Ergebnisse zu erhalten, wenn dies der Fall ist.

4voto

dfa Punkte 110809

Sie können auch die neuer Gabelverbindungsrahmen von Doug Lea . Eines der besten Bücher zu diesem Thema ist sicherlich Java-Gleichzeitigkeit in der Praxis . Ich würde Ihnen dringend empfehlen, einen Blick auf das Gabelgelenkmodell zu werfen.

1voto

pato Punkte 96

Java-Threads sind einfach zu schwerfällig. Wir haben parallele Zweige in Ateji PX als sehr leichtgewichtige geplante Objekte implementiert. Wie in Erlang können Sie zig Millionen parallele Verzweigungen erstellen, bevor Sie einen Overhead bemerken. Aber es ist immer noch Java, Sie müssen also nicht zu einer anderen Sprache wechseln.

0voto

Bill K Punkte 61074

Wenn Sie in Ihren Threads die ganze Zeit eine vollständige Verarbeitung durchführen, werden Sie nicht davon profitieren, mehr Threads als Prozessoren zu haben. Wenn Ihre Threads gelegentlich aufeinander oder auf das System warten, dann skaliert Java gut bis zu Tausenden von Threads.

Ich habe eine Anwendung geschrieben, die ein Klasse-B-Netz (65.000) in wenigen Minuten durch Anpingen der einzelnen Knoten entdeckt hat, wobei jeder Ping mit zunehmender Verzögerung wiederholt wurde. Als ich jeden Ping auf einen separaten Thread gelegt habe (das war vor NIO, ich könnte es jetzt wahrscheinlich verbessern), konnte ich bis zu 4000 Threads in Windows laufen lassen, bevor die Dinge anfingen, schlapp zu machen. Unter Linux lag die Zahl eher bei 1000 (ich habe nie herausgefunden, warum).

Unabhängig davon, welche Sprache oder welches Toolkit Sie verwenden, wenn Ihre Daten interagieren, müssen Sie den Bereichen, in denen dies der Fall ist, besondere Aufmerksamkeit schenken. Java verwendet das Schlüsselwort Synchronized, um zu verhindern, dass zwei Threads gleichzeitig auf einen Abschnitt zugreifen. Wenn Sie Ihr Java auf eine funktionalere Art und Weise schreiben (indem Sie alle Mitglieder zu Finalisten machen), können Sie ohne Synchronisierung arbeiten, aber es kann - nun, sagen wir einfach, dass die Lösung von Problemen auf diese Weise einen anderen Ansatz erfordert.

Java verfügt über weitere Werkzeuge zur Verwaltung unabhängiger Arbeitseinheiten, weitere Informationen finden Sie im Paket "Concurrent".

0voto

Michael Borgwardt Punkte 334642

Java ist ziemlich gut in der Parallelverarbeitung, aber es gibt zwei Vorbehalte:

  • Java-Threads sind relativ schwergewichtig (verglichen mit z.B. Erlang), also fangen Sie nicht an, sie zu Hunderten oder Tausenden zu erstellen. Jeder Thread erhält seinen eigenen Stapelspeicher (Standard: 256 KB), und unter anderem könnte Ihnen der Speicher ausgehen.
  • Wenn Sie auf einem sehr leistungsstarken Rechner arbeiten (insbesondere mit vielen CPUs und einer großen Menge an RAM), dann können die Standardeinstellungen der VM (insbesondere in Bezug auf GC) zu einer suboptimalen Leistung führen und Sie müssen möglicherweise einige Zeit damit verbringen, sie über Befehlszeilenoptionen . Leider ist dies keine einfache Aufgabe und erfordert eine Menge Wissen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X