Thread pausieren, lange while-Schleife

JanStre

Mitglied
Hallo,

ich habe einen Thread programmiert, der ohne die deprecated-methoden auskommt. Bisher konnte ich es so veranlassen, dass der Thread gestartet, pausiert und beendet werden kann.
Ich möchte die Funktionalität dahingehend erweitern, dass nach dem Pausieren der Thread dort weitermacht, wo er aufgehört hat. Das ist u.a. notwendig, wenn in der while-Schleife eine lange Prozedur abgearbeitet wird. Bisher wird das Pausieren damit beendet, dass die while-Schleife wieder oben beginnt und neu anfängt zu arbeiten.

Java:
public class BeendenThread1 {
  static TestThread1 testThread = new TestThread1();
  
  public static void main (String[] args) 
                     throws InterruptedException {
	
	testThread.start();
    Thread.sleep(5500);
    
    testThread.interrupt();
    Thread.sleep(2000);
    TestThread1.pausing = false;
    
    
    Thread.sleep(5000);
    testThread.interruptAndStop();
  }
}

Java:
public class TestThread1 implements Runnable{

  Thread thread = null;
  public static boolean pausing;
  
  public synchronized void start(){
    if (thread == null){
      thread = new Thread(this);
      thread.start();
    }
  }
  
  public synchronized void stop(){
	  if (thread != null)
	    thread = null;
	}
  
  public void interruptAndStop(){
	  interrupt();
	  pausing = false;
	  stop();
  }
  
  public synchronized void interrupt(){
	  pausing = true;
	    if (thread != null)                
	      thread.interrupt();              
	  }
  
  public void sleep(){
	  try {
		  synchronized(this){
			  Thread.sleep(1000);
		  }
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
  }
  
  public void pause() throws InterruptedException{
	  pausing = true;
	  while(pausing){
		  synchronized(this){
			  Thread.sleep(1000);
		  }
		  System.out.println("Thread pausiert weiter");
	  }
  }
  
  public void run(){
    long startZeit = System.currentTimeMillis();
    int i = 1;
    System.out.println("Thread gestartet");
    while (thread != null){
    	try{
    		Thread.sleep(1000);
    		System.out.println(i++);
    	}
    	catch(InterruptedException e){
    		System.out.println("Thread pausiert");
    		try {
    			if(pausing){
    				pause();	
    			}
    			else {break;}
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
    	}
    }
    long dauer = System.currentTimeMillis()-startZeit;
    System.out.println("Thread gestoppt, Lebensdauer: "
                       + dauer);
  }
  
}

Würde der Prozess innerhalb der while-Schleife beispielsweise 10 Sekunden dauern und bei 5 Sekunden wird Pause ausgelöst:
Bisher wird die Pause beendet und die while-Schleife fängt oben bei 0 Sekunden wieder an.
Ziel ist es, bei 5 Sekunden die Arbeit wiederaufzunehmen.
 
ich kann den Code gerade nicht ausprobieren, auf Sicht ist aber nicht nachzuvollziehen, warum i wieder auf 0 gesetzt wird
oder was immmer du mit 'while-Schleife fängt oben bei 0 Sekunden wieder an' meinst (leider ohne Erklärung)

in jedem Fall gilt allgemein, dass du immer und überall die volle Kontrolle über alles hast,
programmiere es so wie du möchtest,
eine Schleife fängt wieder von vorne an, wenn etwa nach einem try/ catch nach 'davor' gesprungen wird, eigentlich nur innerhalb einer zweiten höheren Schleife möglich,
willst du das nicht, dann das try/catch enger setzen oder was immer aktuell los ist
 
Ok, war vielleicht kein passendes Beispiel; hier etwas besseres:

Java:
public void run(){
    long startZeit = System.currentTimeMillis();
    System.out.println("Thread gestartet");
    while (thread != null){
    	try{
    		Thread.sleep(1000);
    		System.out.println("Ausführung1");
    		Thread.sleep(1000);
    		System.out.println("Ausführung2");
    		Thread.sleep(1000);
    		System.out.println("Ausführung3");
    		Thread.sleep(1000);
    		System.out.println("Ausführung4");
    		Thread.sleep(1000);
    		System.out.println("Ausführung5");
    		Thread.sleep(1000);
    		System.out.println("Ausführung6");
    	}
    	catch(InterruptedException e){
    		System.out.println("Thread pausiert");
    		try {
    			if(pausing){
    				pause();	
    			}
    			else {break;}
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
    	}
    }
    long dauer = System.currentTimeMillis()-startZeit;
    System.out.println("Thread gestoppt, Lebensdauer: "
                       + dauer);
  }

das sollte folgende Ausgabe ergeben:
Thread gestartet
Ausführung1
Ausführung2
Ausführung3
Ausführung4
Thread pausiert
Thread pausiert weiter
Thread pausiert weiter
Ausführung1
Ausführung2
Ausführung3
Ausführung4
Thread pausiert
Thread gestoppt, Lebensdauer: 12000


Was ich möchte ist aber, das nach der Pause mit Ausführung5 weiter gemacht wird, bzw. wenn man ganz genau ist, wenn ausführung4 noch nicht vollständig abgearbeitet ist, dort weitermacht.
Mir ist schon klar, dass ich das irgendwie kontrollieren kann, aber bisher hab ich es noch nicht rausgefunden 😉
 
Ja den Ansatz habe ich auch schon verfolgt, aber leider konnte ich das bisher noch nicht implementieren.
Wie muss ich das wait() und notify() denn für diesen Thread anwenden, hab mit den Methoden bisher keine Erfahrung.
 
wait/ notify ist vielleicht schöner, hilft aber nicht unbedingt bei genau deinem Punkt,

es gilt wie ich es gesagt hatte, das try/ catch nicht über die gesamte Befehlsmenge, sondern zwischen jeden Befehl einzeln,

das klingt zunächst aufwendig, aber es gibt ja wie immer die Möglichkeit zu Untermethoden,
schreibe also
Java:
            System.out.println("Ausführung1");
            sleep1000MaybePause();
            System.out.println("Ausführung2");
            sleep1000MaybePause();
            System.out.println("Ausführung3");
            sleep1000MaybePause();
            System.out.println("Ausführung4");
            sleep1000MaybePause();
            System.out.println("Ausführung5");
            sleep1000MaybePause();
            System.out.println("Ausführung6");
mit einer neuen Methode sleep1000MaybePause(); die zunächst nur 1000 ms wartet, per try/catch aber Pause feststellt und dann länger wartet usw., ganz wie im derzeitigen try/catch,
wenn das dann fertig ist, gehts an der richtigen Stelle weiter

das try/catch in run() kann dann wahrscheinlich wegfallen

------

allgemein ist es nicht schön, nach jedem Befehl so ein Sonderkommando zu schreiben,
aber ganz nach jedem muss ja nicht sein, und wenn man wiederholt Untermethoden aufruft, kann man das optimierend zusammenlegen, etwa
Java:
printThanSleep1000MaybePause("Ausführung1");
printThanSleep1000MaybePause("Ausführung2");
printThanSleep1000MaybePause("Ausführung3");
printThanSleep1000MaybePause("Ausführung4");
das mit dem Namen ist natürlich nur zur Erklärung, eher hat man sicher Namen die sich auf die Aufgabe beziehen und dass dann am Ende der Untermethode gewartet wird, muss man sich eben merken

-----

vielleicht will man die Logik-Methoden aber sauber halten, die Thread-Pausen-Steuerung soll eher separat laufen,
noch ein Denkansatz:

es gibt eine Methode
runAndSleepMaybePause(Runnable r);
die das Runnable ausführt (einfach nur ein Objekt mit run-Methode, kein eigener Thread), dann wartet und mit try/ catch vielleicht pausiert,

von der Haupt-Kontrolle des Threas werden Runnable-Objekte erstellt, in denen die Methodenaufrufe stehen und dann runAndSleepMaybePause() aufgerufen,
Runnable-Objekte etwa als anonyme Klassen zu erstellen kann sehr viel unschönen Code erzeugen, mit Schleifen geht es einigermaßen, was in deinem Beispiel denkbar wäre:
Java:
List<Runnable> list = ..
for (i ..) {
  final int finalI = i;
  list.add(new Runnable() {
    public void run() {
      System.out.println("i: "+i);
    }
  });
}

for (Runnable r : list) {
   runAndSleepMaybePause(r);
}
erst Runnable-Objekte erstellen, die noch nix tun, aber die Kommandos enthalten,
dann diese nacheinander an runAndSleepMaybePause() schicken, dort werden sie ausgeführt (run() aufrufen), geschlafen + evtl. pausiert


---------

edit:

einen Abbruch des Threads aus den Untermethoden heraus zur main-Schleife geht mit den mehrfachen einzelnen Befehlen printThanSleep1000MaybePause() per boolean-Rückgabewert und lauter ifs relativ schlecht, viel unnötiger Code, da bietet sich eine RuntimeException an, die dann doch wieder mit try/catch ganz außen gefangen wird,

bei den Runnable in einer Liste, die nur an einer einzigen Code-Stelle ausgeführt werden:
Java:
for (Runnable r : list) {
   runAndSleepMaybePause(r);
}
wäre dagegen möglich den Thread-Abbruch per Rückgabewert false und einem if zu steuern
 
Zuletzt bearbeitet von einem Moderator:
Ich hab mir noch überlegt, das ganze einfach über einen Status der Maschine zu steuern.

Nach jeder "Ausführung" wird der Status inkrementiert, am Ende der while-Schleife wieder resettet. Wenn jetzt innerhalb der while-Schleife ein Interrupt kommt, merkt man sich einfach den Status und nach dem try-catch fängt die while-Schleife praktisch oben an, fängt aber mit der Methode dort an, deren Ausführung noch nicht komplett beendet wurde:
So wie es jetzt aussieht, könnte man das ganze noch in eine sehr einfaches switch-case-konstrukt umwandeln, das jedesmal in der while-schleife abgelaufen wird.

Java:
    while (thread != null){
    	try{
    		if(status == 0){
    			Thread.sleep(1000);
    			System.out.println("Ausführung1");
    			status++;
    		}
    		if(status == 1){
    			Thread.sleep(1000);
    			System.out.println("Ausführung2");
    			status++;
    		}
    		if(status == 2){
    			Thread.sleep(1000);
    			System.out.println("Ausführung3");
    			status++;
    		}
    		if(status == 3){
    			Thread.sleep(1000);
    			System.out.println("Ausführung4");
    			status++;
    		}
    		if(status == 4){
    			Thread.sleep(1000);
    			System.out.println("Ausführung1");
    			status++;
    		}
    	}
    	catch(InterruptedException e){
    		System.out.println("Thread pausiert");
    		try {
    			if(pausing){
    				pause();	
    			}
    			else {break;}
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
    	}
    	status = 0;
    }
 
durchaus denkbar, könnte man meiner Runnable-Version zuordnen, hier in dem einfachen Fall mit nur einer Art Aktion ist diese direkt angegeben,

auch wenn es sicher nur ein Beispiel ist, lohnt es sich immer die Optimierungen zu betrachten:
> Thread.sleep(1000);
+
> status++;
stehen in jedem if, die können aus den ifs raus und nur einmal am Anfang bzw. Ende der Schleife stehen,

die System.out.println-Kommandos hängen direkt vom status ab, da muss kein if oder switch her,
ein direkter Befehl berechnet das richtig:
> System.out.println("Ausführung"+(status+1));
 
ja die konzepte sind klar, waren ja auch nur anschauliches material um die ausführungszeit zu simulieren, dahinter stehen methoden, die durchaus mal 5-10 Sekunden dauern können (nicht wegen rechenzeit, sondern auch wegen wartezeiten ihrereseits)

ich schließ den thread mal, ich hab mit dem status-konstrukt meine lösung gefunden.
 

Zurück
Oben