Methoden Countdown

Andre267

Mitglied
Schönen guten Abend,

ich versuche gerade einen kleinen Countdown zu programmieren der in Echtzeit jede Sekunde halt runter zählt. Problem ist nur, dass sich bei mir meine GUI aufhängt und einfach nicht mehr erreichbar ist bis der Countdown vorbei ist.

Hier mal meine Methode:
Java:
    public String countdownstart(){
        for(int i=0; i<this.time; i++){
            int value = this.time-i;
            return "Noch: " + value + " Sekunden\n";
        }
        return "";
    }

Weiß jemand was ich falsch mache?
 
G

Gast2

Gast
Die Schleife ist unnötig, die beendest du ja direkt im ersten Durchlauf.

Zu deiner GUI:
Du lässt die Schleife vermutlich auf dem EDT laufen (der ist für das Darstellen deiner GUI zuständig). Du musst Operationen die länger dauern, wie beispielsweise deinen Countdown, in einem separaten Thread ausführen.
 

Andre267

Mitglied
Wie die schleife kann ich weg lassen und wie mache ich das es immer 1 Sekunde Abstand ist?
Irgendwie muss ich doch das runter zählen lassen.
 

jemandzehage

Aktives Mitglied
Moin

Folgendes Hauptproblem:
Wie eikeB schon versucht hat dir zu sagen ist das ein Problem der Threads. Du brauchst einen eigenen Thread, der deine GUI aktualisierst. Wenn du in diesem Thread irgendwelche wartenden bzw. blockierenden Funktionen verwendest, kann die GUI in dieser Zeit nicht neu gezeichnet werden. Das sieht dann so aus als hätte sich das Programm aufgehängt. Starte deine for-Schleife also einfach in einem neuen Thread:

Java:
new Thread(new Runnable() {
  public void run() {
    //hier Funktion zum Countdown aufrufen
  }
}).start();

Was ebenfalls noch fehlerhaft erscheint ist, dass du einen Return-Aufruf innerhalb einer Schleife hast. Dadurch wird die Schleife terminiert.

Als letztes steht bei deiner Ausgabe von value Sekunden. Allerdings wartet deine Schleife nicht. Also ist zwischen zwei Zeiteinheiten nur die Zeit von einem Schleifendurchlauf und nicht von einer Sekunde. Dazu kannst du z.B. deinen Thread eine Sekunde schlafen lassen (Thread.sleep...)
 
S

Spacerat

Gast
:idea:
Java:
import java.lang.Thread.State;
import java.util.HashSet;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

public final class CountDown {
	public enum EventType {
		START,
		STOP,
		PAUSE,
		UPDATE,
	}
	private final Object stateChangeSync = new Object();
	private final Observable dispatcher = new Observable() {
		@Override
		public void notifyObservers(Object obj) {
			setChanged();
			super.notifyObservers(obj);
		}
	};
	private final Set<Thread> waitingTasks = new HashSet<>();
	private final long timeOut;
	private long startTime, lastTime;
	private Thread runner;
	private boolean pause;

	public CountDown(long timeOut) {
		if (timeOut <= 0) {
			throw new IllegalArgumentException("timeOut has tobe larger than 0");
		}
		this.timeOut = timeOut;
	}

	public void reset() {
		startTime = lastTime = System.currentTimeMillis();
	}

	public void pause() {
		pause(!pause);
	}

	public void pause(boolean set) {
		synchronized (stateChangeSync) {
			if (runner != null) {
				synchronized (runner) {
					if (set && runner.getState() != State.WAITING) {
						try {
							runner.wait();
						} catch (InterruptedException e) {
							// ignore
						}
					} else if (!set && runner.getState() == State.WAITING) {
						reset();
						runner.notify();
					}
				}
			}
			if(pause != set) {
				dispatcher.notifyObservers(EventType.PAUSE);
			}
			pause = set;
		}
	}

	public void start() {
		synchronized (stateChangeSync) {
			if (runner == null) {
				runner = new Thread("Countdown") {
					private long time;
					@Override
					public void run() {
						while (!isInterrupted() && lastTime - startTime <= timeOut) {
							time = System.currentTimeMillis();
							if(time != lastTime) {
								lastTime = time;
								dispatcher.notifyObservers(EventType.UPDATE);
							}
						}
						runner = null;
						notifyWaiting();
						dispatcher.notifyObservers(EventType.STOP);
					}
				};
				reset();
				pause(false);
				runner.start();
				dispatcher.notifyObservers(EventType.START);
			}
		}
	}

	public void stop() {
		synchronized (stateChangeSync) {
			if (runner != null) {
				synchronized (runner) {
					runner.notify();
					runner.interrupt();
					try {
						runner.join();
					} catch (InterruptedException e) {
						// ignore
					}
					runner = null;
					notifyWaiting();
					dispatcher.notifyObservers(EventType.STOP);
				}
			}
		}
	}

	public void join() {
		Thread t = Thread.currentThread();
		if(waitingTasks.add(t)) {
			synchronized (t) {
				try {
					t.wait();
				} catch(InterruptedException e) {
					// ignore
				}
			}
		}
		
	}

	public void addObserver(Observer obs) {
		dispatcher.addObserver(obs);
	}

	public void removeObserver(Observer obs) {
		dispatcher.deleteObserver(obs);
	}

	@Override
	public String toString() {
		long days = lastTime - startTime;
		days = timeOut - days;
		double tmp = days / 86400000.0;
		days = (long) tmp;
		tmp -= days;
		tmp *= 24;
		long hours = (long) tmp;
		tmp -= hours;
		tmp *= 60.0;
		long minutes = (long) tmp;
		tmp -= minutes;
		tmp *= 60.0;
		long seconds = (long) tmp;
		tmp -= seconds;
		long millis = (long) (tmp * 1000.0);
		return String.format("%d days, %02d:%02d:%02d:%03d", days, hours, minutes, seconds, millis);
	}

	private void notifyWaiting() {
		for(Thread t : waitingTasks) {
			synchronized (t) {
				t.notify();
			}
		}
	}

	public static void main(String[] args) {
		final CountDown cd = new CountDown(60000);
		cd.addObserver(new Observer() {
			@Override
			public void update(Observable o, Object arg) {
				if(arg instanceof EventType) {
					switch((EventType) arg) {
					case PAUSE:
						break;
					case START:
						break;
					case STOP:
						break;
					case UPDATE:
						System.out.println(cd);
						break;
					}
				}
			}
		});
		cd.start();
		cd.join();
	}
}
Nur mal sooo... ;)
Lässt sich im übrigen auch ganz simpel in z.B. JLabels als Text setzen.
Java:
new JLabel().setText(System.valueOf(cd));
 

Andre267

Mitglied
Ich habe die Methode countdown jetzt so geändert:
Java:
    public void countdownstart() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < time; i++) {
                    int value = time - i;
                    return "Noch: " + value + " Sekunden\n";
                }
            }
        }).start();
    }

Jetzt sagt er aber das die methode run nicht void sondern String haben muss...
Wenn ich das auf String setze gibt es 1000 weitere Fehler. ???:L

Was mach ich falsch?

PS: Danke für die Klasse da, aber ich verstehe davon fast noch nix deswegen bringt mir die nix sry...
 
G

Gast2

Gast
Du deklarierst eine Methode, die keinen Rückgabewert hat (void), aber du versuchst einen String zurückzugeben.

Anstatt da was zurückzugeben, solltest du den Text auf ein JLabel oder ähnliches schreiben.
 
G

Gast2

Gast
Das geht aber so wie du dir was vorstellst nicht.
1) Du kannst aus einem Thread nichts zurückgeben
2) Das Konstruktor mit der Schleife und dem return geht nicht

Poste doch mal was du genau vor hast, gerne auch mit entsprechendem Code.
 
S

Spacerat

Gast
Ich will den Text an meinen Server senden und der wird dann an entsprechende Clients gesendet.
Das ginge mit meiner Klasse zwar auch, aber wenn die Clients dann auch noch uptodate gehalten werden sollen, hättest du jede Menge Traffic.
In diesem Fall würde man eher eine Targettime ("System.currentTimeMillis() + timeOut") an die Clienten senden welche ihrerseits mit "targetTime - System.currentTimeMillis()" eigene Countdowns starten. So wären dann auch alle Clienten synchronisiert, weil diese Rechnerei die Netzwerk-Latenzen automatisch abzieht.
 

Ähnliche Java Themen

Neue Themen


Oben