N00b-Frage: Thread von außen anhalten und wieder anstoßen
-
Hallo,
ich habe hier ein Problem, dass ich meiner Unkenntnis der Java-Threading-Eigenheiten zuschreibe.
Mein Thread liest eine Datei zeilenweise ein und wartet dabei zwischen zwei Lesevorgängen um einen definierten Zeitbetrag (der sich aus dem gelesenen Inhalt ergibt, aber das ist nebensächlich). Zusätzlich möchte ich den Thread über einen Methodenaufruf komplett anhalten können, und zwar solange, bis eine andere Methode aufgerufen wird.
Derzeit habe ich das so:
public class Foo { private Thread m_thread; private Runnable simulatorRunnable = new Runnable() { public void run() { // get time diff to sleep long sleep_time = ...; try { System.out.println("sleeping for " + sleep_time + "ms"); Thread.sleep(sleep_time); // geht hier auch wait? } catch (InterruptedException e) { e.printStackTrace(); } } }; public void startSimulation(Float speed) { m_thread = new Thread(simulatorRunnable, "Simulator"); if (speed > 0) m_thread.start(); } public void pauseSimulation() { try { // warte solange, bis jemand resumeSimulation() aufruft m_thread.wait(); } catch (Exception e) { e.printStackTrace(); } } public void resumeSimulation() { try { m_thread.notify(); } catch (Exception e) { e.printStackTrace(); } } };
Das Problem ist nun, das der Thread mit der richtigen "Geschwindigkeit", also korrekten Pausen liest, das wait/notify aber zu einer illegalMonitorStateException führt. Die Java-Referenz meint dazu, dass das nur in einm synchrnonisierten Block geht, aber wenn ich den m_thread.wait() call in einen synchronized(m_thread)-Block einpacke, bleibt das ganze Programm stehen und nicht nur der Thread, also kann ich auch keinen resume-Aufruf absetzen, der notify()-machen würde.
Ich hoffe, ihr könnt einem absoluten Java-Thread n00b weiterhelfen.
Gruß
Phil
-
Die wait()-Methode gehört nicht zum Thread, sondern zur Klasse Object. Und sie lässt immer den Thread warten, der sie aufruft, solange bis ein anderer Thread auf demselben Objekt notify() aufruft.
Du musst dir eine Möglichkeit schaffen, dass dein Runnable-Objekt selbst das wait() aufruft (innerhalb der run()-Methode), zum Beispiel über ein Memberflag, dass du von außen setzt. Wenn er weiterlaufen soll, das Flag löschen und notify() aufrufen.
-
Dasd schrieb:
... solange bis ein anderer Thread auf demselben Objekt notify() aufruft.
Aber genau das führt doch zur illegal monitor state exception ??? *verwirrt*
-
PhilippM schrieb:
Aber genau das führt doch zur illegal monitor state exception ??? *verwirrt*
Deswegen synchronized. Ich habe mal schnell was zusammengeschustert:
public class Test { private static void safeSleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e1) { e1.printStackTrace(); } } public static class TestRunnable implements Runnable { private boolean running = true; private boolean paused = false; @Override public void run() { while (isRunning()) { // wichtige Sachen machen System.out.println("Arbeite..."); safeSleep(1000); if (isPaused()) { // Pause erwünscht System.out.println("Pause beginnen"); synchronized (this) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Pause beendet"); } } System.out.println("Verlasse run()..."); } public synchronized boolean isPaused() { return paused; } public synchronized void pause() { paused = true; } public synchronized void resume() { paused = false; notify(); } public synchronized void stop() { running = false; } public synchronized boolean isRunning() { return running; } } public static void main(String[] args) { TestRunnable run = new TestRunnable(); new Thread(run).start(); safeSleep(2000); // ... irgendwann ... run.pause(); safeSleep(2000); // ... noch später ... run.resume(); safeSleep(2000); run.stop(); } }
Erzeugt folgende Ausgabe:
Arbeite... Arbeite... Pause beginnen Pause beendet Arbeite... Arbeite... Arbeite... Verlasse run()...
-
Danke. Entspricht in etwa dem, was die Java-Referenz zum ersetzen der deprecated methods Thread.pause() und Thread.resume() empfiehlt.
So hab ichs jetzt auch gemacht.