ThreadPool mit Pause usw.

T

threadExec

Gast
Hallo,

ich habe mich heute mit einem kleinen Programm rumgeschlagen,
um zu testen wie ich am besten einen Threadpool verwende und
gleichzeitig die im Threadpool enthaltenen Thrads pausieren und
stoppen kann.

Ich habe nun allerdings das Problem, dass sich das Programm
nach dem Fortsetzen (d. h. Fortsetzen nachdem ich es pausiert habe)
nicht mehr bedienbar ist.

Auch zuvor läuft es nicht wirklich flüssig.

Ein kleines kompilierbares Beispiel:

Java:
public class MyRunnable implements Runnable {
	private boolean isWaiting = false;
	private boolean isStopped = false;
	private String name = "";
	
	public MyRunnable(String name) {
		this.name = name;
	}

	public void run() {
		int i = 0;
		
		while(!isStopped) {
			isWaiting();
				
			System.out.println("Running: " + name + " , Counter: " + i);
				
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				System.out.println("Interrupted: " + name);
			}
			
			i++;
		}
	}
	
	/**
	 * 
	 */
	public synchronized void pauseProcess() {
		isWaiting = true;
		System.out.println("Paused: " + name);
	}
	
	/**
	 * 
	 */
	public synchronized void continueProcess() {
		isWaiting = false;
		notify();
	}
	
	/**
	 * 
	 */
	private synchronized void isWaiting() {
		if(isWaiting) {
			try {
				wait();
			} catch (InterruptedException e) {}
		}
	}
	
	public synchronized boolean getIsWaiting() {
		return isWaiting;
	}
	
	public synchronized void setIsWaiting(boolean isWaiting) {
		this.isWaiting = isWaiting;
	}
	
	public synchronized boolean getIsStopped() {
		return isStopped;
	}
	
	public synchronized void setIsStopped(boolean isStopped) {
		this.isStopped = isStopped;
	}
}

Java:
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Controller extends Thread {
	private Queue<String> queue = null;
	private ExecutorService exec = null;
	private List<MyRunnable> runnables = null;
	private boolean isStopped = false;
	private int index = 0;
	
	public Controller(Queue<String> queue) {
		this.queue = queue;
		this.exec = Executors.newFixedThreadPool(10);
		this.runnables = new LinkedList<MyRunnable>();
		
		while(!queue.isEmpty()) {
			runnables.add(new MyRunnable(queue.poll()));
		}
	}
	
	public void run() {
		while(!runnables.isEmpty() && !isStopped) {
			exec.execute(runnables.get((index < 998 ? index++ : index)));
		}
	}
	
	public void pauseProcess() {
		System.out.println("Controller triggered Pause!");
			
		for(int i = 0; i < index; i++) {
			System.out.println("Controller Pause: " + i);
			runnables.get(i).setIsWaiting(true);
		}
	}
	
	public void resumeProcess() {
		for(int i = 0; i < index; i++) {
			System.out.println("Controller Resume: " + i);
			runnables.get(i).setIsWaiting(false);
		}
	}
	
	public void stopProcess() {
		isStopped = true;
				
		for(int i = 0; i < index; i++) {
			runnables.get(i).setIsStopped(true);
		}
	}
}

Java:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class View extends JFrame implements ActionListener {
	private Controller controller = null;
	private JButton buttonStart = new JButton("Start");
	private JButton buttonPause = new JButton("Pause");
	private JButton buttonResume = new JButton("Resume");
	private JButton buttonStop = new JButton("Stop");
	
	public View(Controller controller) {
		this.controller = controller;
		
		buttonStart.addActionListener(this);
		buttonPause.addActionListener(this);
		buttonResume.addActionListener(this);
		buttonStop.addActionListener(this);
		
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setMinimumSize(new Dimension(400, 400));
		this.setBounds(100, 100, 400, 400);
		this.setLayout(new GridLayout(1, 0));
		this.setVisible(true);

		this.add(buttonStart);
		this.add(buttonPause);
		this.add(buttonResume);
		this.add(buttonStop);
	}

	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		
		if(source.equals(buttonStart)) {
			Thread thread = new Thread(controller);
			thread.start();
		} else if(source.equals(buttonPause)) {
			controller.pauseProcess();
		} else if(source.equals(buttonResume)) {
			controller.resumeProcess();
		} else if(source.equals(buttonStop)) {
			controller.stopProcess();
		}
	}
}

Java:
import java.util.LinkedList;
import java.util.Queue;


public class Main {
	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<String>();
		
		for(int i = 0; i < 1000; i++) {
			queue.offer("" + i);
		}
		
		new View(new Controller(queue));
	}
}

Kann mir jemand sagen wo da das Problem ist?

Nebenbei noch eine Frage, ich dachte, dass der Threadpool immer nur
so viele Runnables annimmt, wie der Threadpool groß ist (in meinem Falle 10),
wenn man nun aber Pause klickt sieht man am output in der Konsole, das
alle 1000 bereits pausiert werden (ich gehe da ja immer bis zum index
und wenn der Threadpool wirklich nur immer 10 übernehmen würde wäre
der index zumindest beim ersten mal noch 10)

Wäre cool wenn mir auch das jemand erläutern könnte :)
 
S

SlaterB

Gast
deine Schleife
Java:
        while(!runnables.isEmpty() && !isStopped) {
            exec.execute(runnables.get((index < 998 ? index++ : index)));
        }
läuft sofort durch bis zu den Tausend und ewig weiter, sorgt für 100% CPU-Belastung, was die GUI lahm werden läßt,
übergib einfach alle per for-Schleife bis 1000 und gut ist, einen Controller-Thread brauchst du nicht,

freilich würden die Methoden dann alle auch bis 1000 arbeiten,
das mit dem Index ist schon besser, aber nur hochzählen ist auch nicht gut, dann hättest du ja spätestens am Ende viele Runnables zu bearbeiten obwohl auch dann nur 10 aktiv sind,
verzichte vielleicht doch auf index,

lasse die MyRunnables sich zu Beginn und Ende ihrer run-Methode in einer Liste der aktuellen Aufgaben ein- und austragen,
auf Synchronisation achten,
wenn nötig die Start/ Stop-Methoden solange warten lassen bis 10 MyRunnables angemeldet sind,
damit nicht versehentlich Pause geklickt wird wenn erst 9 von 10 greifbar sind usw.
 
T

threadExec

Gast
Guten Morgen!

Habe deine Tipps von gestern wie folgt umgesetzt und alles funktioniert
prächtig. Hoffe, dass es auch "guter" Cose ist :)

Java:
public class MyRunnable implements Runnable {
	private Controller controller = null;
	private boolean isWaiting = false;
	private boolean isStopped = false;
	private String name = "";
	
	public MyRunnable(Controller controller, String name) {
		this.controller = controller;
		this.name = name;
	}

	public void run() {
		controller.registerService(this);
		
		int i = 0;
		
		while(!isStopped) {
			isWaiting();
				
			System.out.println("Running: " + name + " , Counter: " + i);
				
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				System.out.println("Interrupted: " + name);
			}
			
			i++;
		}
		
		controller.removeService(this);
	}
	
	public synchronized void pauseProcess() {
		isWaiting = true;
		System.out.println("Paused: " + name);
		
	}
	
	public synchronized void continueProcess() {
		isWaiting = false;
		notify();
		System.out.println("Resumed: " + name);
	}
	
	public synchronized void stopProcess() {
		isStopped = true;
	}
	
	private synchronized void isWaiting() {
		if(isWaiting) {
			try {
				wait();
			} catch (InterruptedException e) {}
		}
	}
	
	public synchronized boolean getIsWaiting() {
		return isWaiting;
	}
	
	public synchronized void setIsWaiting(boolean isWaiting) {
		this.isWaiting = isWaiting;
	}
	
	public synchronized boolean getIsStopped() {
		return isStopped;
	}
	
	public synchronized void setIsStopped(boolean isStopped) {
		this.isStopped = isStopped;
	}
}

Java:
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Controller extends Thread {
	private Queue<String> queue = null;
	private ExecutorService exec = null;
	private List<MyRunnable> service = null;
	
	public Controller(Queue<String> queue) {
		this.queue = queue;
		this.exec = Executors.newFixedThreadPool(10);
		this.service = new ArrayList<MyRunnable>();
	}
	
	public void run() {
		for(int i = 0; i < queue.size(); i++) {
			exec.execute(new MyRunnable(this, queue.poll()));
		}
	}
	
	public void pauseProcess() {
		for(int i = 0; i < service.size(); i++) {
			service.get(i).pauseProcess();
		}
	}
	
	public void resumeProcess() {
		for(int i = 0; i < service.size(); i++) {
			service.get(i).continueProcess();
		}
	}
	
	public void stopProcess() {
		for(int i = 0; i < service.size(); i++) {
			service.get(i).stopProcess();
		}
		
		exec.shutdown();
		exec.shutdownNow();
	}
	
	public synchronized void registerService(MyRunnable runnable) {
		service.add(runnable);
	}
	
	public synchronized void removeService(MyRunnable runnable) {
		service.remove(runnable);
	}
}

Java:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class View extends JFrame implements ActionListener {
	private static final long serialVersionUID = 1L;
	private Controller controller = null;
	private JButton buttonStart = new JButton("Start");
	private JButton buttonPause = new JButton("Pause");
	private JButton buttonResume = new JButton("Resume");
	private JButton buttonStop = new JButton("Stop");
	
	public View(Controller controller) {
		this.controller = controller;
		
		buttonStart.addActionListener(this);
		buttonPause.addActionListener(this);
		buttonResume.addActionListener(this);
		buttonStop.addActionListener(this);
		
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setMinimumSize(new Dimension(400, 200));
		this.setBounds(100, 100, 400, 200);
		this.setLayout(new GridLayout(1, 0));
		this.setVisible(true);

		this.add(buttonStart);
		this.add(buttonPause);
		this.add(buttonResume);
		this.add(buttonStop);
	}

	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		
		if(source.equals(buttonStart)) {
			Thread thread = new Thread(controller);
			thread.start();
		} else if(source.equals(buttonPause)) {
			controller.pauseProcess();
		} else if(source.equals(buttonResume)) {
			controller.resumeProcess();
		} else if(source.equals(buttonStop)) {
			controller.stopProcess();
		}
	}
}

Java:
import java.util.LinkedList;
import java.util.Queue;


public class Main {
	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<String>();
		
		for(int i = 0; i < 1000; i++) {
			queue.offer("" + i);
		}
		
		new View(new Controller(queue));
	}
}
 
S

SlaterB

Gast
Controller muss nicht mehr unbedingt ein Thread sein, die Schleife in der run-Methode dürfte eh nur paar ms laufen,

die Synchronisierung könnte man noch hochfahren, so wichtig ist das freilich nicht,
wenn genauer, dann auch wenigstens Methoden wie pauseProcess() synchronized,

setVisible(true); muss unbedingt letzter Befehl im View-Konstruktor sein, hinter allen GUI-Modifikation,
sonst kann es Anzeigefehler geben,

und ziemlich fragwürdig:
im Moment funktioniert der Start-Button nicht mehr nach einem Stop,
vielleicht auch gar nicht geplant, dann aber verhindern dass es da häßliche Exceptions gibt,
nicht mehr starten wenn schon gestoppt gewesen

mehr als die ersten 10 von 1000 Tasks können gar nicht drankommen,
falls die keine Endlosschleife haben sondern zu beliebigen Zeiten beendet werden muss man wie gesagt
bisschen komplizierter mit der Abstimmung arbeiten,

ein MyRunnable-Task könnte in Zeile 31 stehen, hinter der Schleife vor dem Abmelden, oder schon aus der Liste entfernt sein und noch kein neuer Task angemeldet,
wenn dann 9 pausiert werden kann ein 10. neu dazukommen und noch laufen,
mehr Synchronisierung kann teilweise helfen, auch im Controller den aktuellen Status hinterlegen,
so dass jeder neue Task da nachschauen kann,
'wie ist denn gerade die Lage?, oh, ich habe den pauseProcess()-Durchlauf verpasst, mich erst kurz danach angemeldet,
aber der Controller steht aktuell auf pause, da stelle ich zu Beginn meiner run-Methode selber isWaiting auf true und warte dann'

darauf verzichten kann man wenn man besser aufpasst, dass es keine Übergänge zwischen den Methoden gibt, eh relativ theoretisch, ich will mal auch dazu nicht zuviel schreiben


bei stopProcess() verzichtest du auf ein notify(), was ist wenn gerade pausiert ist?
aber klappt ja anscheinend, der beendete ExecutorService schickt wohl vorteilhafterweise ein notify()
 
Zuletzt bearbeitet von einem Moderator:
T

threadExec

Gast
Wow, habe noch nie so hilfreiche Antworten bekommen, echt vielen Dank :)

Dem Problem, dass sich ein Runnable regestriert nachdem Pause getriggert wurde,
würde ich so entgegen wirken:

Java:
controller.registerService(this);
		
if(controller.isWaiting()) {
	pauseProcess();
}

Es ist nun also der Status auch im Controller gesetzt.

Das mit der Endlosschleife im MyRunnable war Absicht in diesem Beispiel.
Wenn du bei stopProcess() das exec.shutDown() und exec.shutDownNow()
mal rauslöschst wirst du sehen, dass dann immer die nächsten 10 MyRunnables
gestartet werden wenn man stopProcess() triggered.

Nochmal vielen Dank SlaterB ;)
 

KrokoDiehl

Top Contributor
Ich habe gestern per Zufall einen "PausableThreadPool" in der API gefunden und poste es hier mal ohne den ganzen Thread gelesen zu haben. Ich hoffe mir unterläuft also kein Faux-pas ;-)

Hier in der Klassenbeschreibung wird am Ende ein solcher, pausierbarer Pool vorgestellt.
 
S

SlaterB

Gast
Das mit der Endlosschleife im MyRunnable war Absicht in diesem Beispiel.
Wenn du bei stopProcess() das exec.shutDown() und exec.shutDownNow()
mal rauslöschst wirst du sehen, dass dann immer die nächsten 10 MyRunnables
gestartet werden wenn man stopProcess() triggered.
hier würde dann wie gesagt relevant werden, dass das notify() fehlt:
stehen die Tasks auf Pause passiert bei stop() nicht viel, nur boolean-Werte,
kann man natürlich ok finden, erst bei resume() dann wirklich beendet und neue fangen an
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
L Threads Frage zu ThreadPool Java Basics - Anfänger-Themen 2
N ThreadPool Java Basics - Anfänger-Themen 4
N Threadpool Java Basics - Anfänger-Themen 34
T Threadpool? Java Basics - Anfänger-Themen 5
CptK Klassen Event bei gedrückter Maus immer wieder mit Pause ausführen Java Basics - Anfänger-Themen 2
R Threads Pause zwischen zwei Schleifen Java Basics - Anfänger-Themen 1
S Sound stoppen und nach Pause wieder abspielen Java Basics - Anfänger-Themen 6
K Methoden Pause drücken klappt nur jedes 2. Mal Java Basics - Anfänger-Themen 6
X Pause erstellen? Java Basics - Anfänger-Themen 16
G Pause-Funktion in einem mp3-Player Java Basics - Anfänger-Themen 2
J Mach mal ne Pause Java Basics - Anfänger-Themen 9
Q Wie kann man eine kleine pause im Programm machen? Java Basics - Anfänger-Themen 13
P Fragen nach 12 monatiger Java Pause :P Java Basics - Anfänger-Themen 21
P Suche Pause-Methode für for-Schleifen Java Basics - Anfänger-Themen 8
B Pause einfügen? Java Basics - Anfänger-Themen 5
M Pause machen Java Basics - Anfänger-Themen 5
G Schleife soll eine Pause machen Java Basics - Anfänger-Themen 2
F Sleep, pause, delay Java Basics - Anfänger-Themen 2
M Kurze Pause ins Programm einbauen Java Basics - Anfänger-Themen 5
V Pause auch ohne Threads? Java Basics - Anfänger-Themen 6
H Pause um Programm anzuhalten Java Basics - Anfänger-Themen 9
H Pause einfügen Java Basics - Anfänger-Themen 4
S wie bastelt man eine pause-taste Java Basics - Anfänger-Themen 5

Ähnliche Java Themen

Neue Themen


Oben