Java Threads (mal wieder)

Hallo zusammen,

ich habe mal wieder ein kleines Problem mit Java Threads. Ich muss sagen wir mal 100 mal die gleiche sehr zeitaufwändige Berechnung durchführen und dachte mir, das wäre ja toll, wenn ich das auf die 8 Prozessoren die ich zur Verfügung habe aufteilen könnte, so dass ich quasi 8 Berechnungen parallel durchführen kann. Um das Vermitteln eines neuen Threads an eine „freie“ CPU kümmert sich ja die VM soweit ich das in Erfahrung bringen konnte.

Also, was ich bisher gemacht habe: ich habe 2 Klassen geschrieben. Eine (Klasse1) implementiert Runnable und führt in der überschriebenen run() die Berechnung durch. Das passiert mit einem Systemcall über „Runtime.getRuntime().exec(…);“. Die zweite Klasse (Klasse2) startet 8 Threads in folgender Art:

Klasse1 runner = new Runner(this, …);
Thread thread = new Thread(runner);
thread.run();

Klasse2 verfügt über eine Methode, die die nächste Berechnung auf die gleiche Art und Weise startet, wenn sie aufgerufen wird. Dieser Aufruf wird von Klasse1 am Ende der run() aufgerufen, was ja nicht schwer ist, da Klasse1 durch die Übergabe von „this“ aus Klasse2 auf die gleiche Instanz von Klasse2 zugreifen kann.

Die Idee ist, dass 8 Threads gleichzeitig laufen und rechnen, und wenn einer fertig ist, ruft er den nächsten auf.

Problem: es wird nicht auf den Systemcall gewartet. Klasse1 macht den Aufruf des externen Programms und startet direkt den nächsten Thread durch Aufruf der entsprechenden Methode in Klasse2. Wenn ich das ganze in Klasse1 in einen Prozess packe und warte, bis der fertig ist:

Process proc = Runtime.getRuntime().exec(…);
proc.waitFor();

passiert etwas, was ich dachte durch die Threads umgangen zu haben: es wird von den 8 Threads die ich eigentlich starten will nur der erste gestartet (wie oben beschrieben), und Klasse2 wartet dann mit dem Starten des nächsten Threads, bis dieser eine fertig ist. Ich dachte, wenn ich die Berechnung in einen eigenen Thread packe, würde das Programm, das ihn aufgerufen hat, einfach weiterlaufen und eben gerade nicht mehr auf ihn warten.

Hat jemand eine Idee, was hier schief läuft, oder wie ich das Problem (eventuell anders) lösen könnte?

Das zu beschreiben ist irgendwie kompliziert, von daher hoffe ich auch mal, dass mir das irgendwie gelungen ist :wink:

Dankeschön schonmal & Gruß
Schorsch

Moien

Klasse1 runner = new Runner(this, …);
Thread thread = new Thread(runner);
thread.run();

Da liegt der Fehler. Das muss thread. start (); sein.

cu

Aaaaahhhh,

so simpel und doch so eine große Wirkung - vielen Dank (mal wieder).

Viele Grüße
Schorsch

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo,

ich bins es nochmal, mit einem neuen Problem zur gleichen Aufgabenstellung. Das Problem ist jetzt, dass ich eine gewünschte Anzahl von Threads starten kann, aber leider rufen die keine neuen auf, wenn sie eigentlich mit der Arbeit fertig sind. Ich glaube, es ist besser, wenn ich diesmal ein bisschen mehr Code angebe.

Hier ist die Methode, mit der ich neue Threads starte:

public void startNextThread() {
 if (combinations.size() \> 0) {
 String commandLine= combinations.get(0);
 combinations.remove(0);
 Runner runner = new Runner(this, commandLine);
 Thread thread = new Thread(runner);
 thread.start(); 
 } else {
 System.out.println("\>\> list empty - all done");
 }
}

„combinations“ ist dabei eine ArrayList, in der ich alle Aufrufe für die Kommandozeile, die ich später abarbeiten möchte, gespeichert habe.

Meine Klasse, die Runnable implementiert und in der letztendlich „gearbeitet“ wird:

public class Runner implements Runnable {

 private String commandLine;
 private Launcher launcher;

 public Runner(Launcher launcher, String commandLine) {
 this.launcher = launcher;
 this.commandLine = commandLine;
 }

 public void run() {
 try {
 Runtime.getRuntime().exec(commandLine);

 Process proc = Runtime.getRuntime().exec(this.commandLine);

 proc.waitFor();

 System.out.println("\>\> done");

 this.launcher.startNextThread();
 } catch (Exception ex) {
 ex.printStackTrace();
 }
 }
}

„launcher“ ist eine Instanz der Klasse, in der sich die oben beschriebene Methode „startNextThread()“ befindet. Also eigentlich soll der Runner seinen Aufruf abarbeiten, warten bis der fertig ist, und dann den nächsten Thread aufrufen. Allerdings kommt er niemals bis zum System.out. Der Programmaufruf vorher funktioniert einwandfrei und macht auch genau was er soll, aber danach geht es eben einfach nicht weiter, der wartet sich irgendwie tot.

Hat jemand eine Idee, woran das liegen könnte und wie man das behebt?

Viele Grüße
Schorsch

Und nochmal ich,

ich bin mit dem Verwalten von mehreren Threads nicht wirklich vertraut, daher das etwas wirre Posten… . Gehe ich recht in der Annahme, dass ich meine Methode

public void startNextThread() {
 if (combinations.size() \> 0) {
 String commandLine= combinations.get(0);
 combinations.remove(0);
 Runner runner = new Runner(this, commandLine);
 Thread thread = new Thread(runner);
 thread.start();
 } else {
 System.out.println("\>\> list empty - all
done");
 }
}

, die die einzelnen Threads startet, synchronized machen sollte?

Viele Grüße
Schorsch

Hi,

ja, die Methode müsste synchronized sein; sonst kann es passieren, dass manche Elemente aus combinations doppelt ausgeführt werden und manche gar nicht.

Ich würde dir aber für dein Problem die Klassen aus dem Paket java.util.concurrent ans Herz legen: Dann musst du dich weder um die genauen Details beim Starten von Threads kümmern noch um die Synchronisation, und die etwas hässliche Abhängigkeit von Runner zurück zu Launcher fällt auch weg:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
**import java.util.concurrent.Executor;  
import java.util.concurrent.Executors;**  

public class Launcher {

 public static void main(String[] args) {
 Launcher launcher = new Launcher();
 launcher.run();
 }

 private static final int MAX\_THREADS = 8;
 private List combinations;

 public Launcher() {
 this.combinations = Collections.nCopies(30, "ls");
 }

 public void run() {
 **Executor e = Executors.newFixedThreadPool(MAX\_THREADS);  
 for (String commandLine : combinations) {  
 e.execute(new Runner(commandLine));  
 }**  
 }
}

class Runner implements Runnable {

 private String commandLine;

 public Runner(String commandLine) {
 this.commandLine = commandLine;
 }

 public void run() {
 try {
 Process proc = Runtime.getRuntime().exec(this.commandLine);

 proc.waitFor();

 System.out.println("\>\> done " + Thread.currentThread().getId());
 } catch (Exception ex) {
 ex.printStackTrace();
 }
 }
}

Gruß,
Andreas

Dankeschön :smile: