# Auf TimerTask warten?



## SaschaB (9. Februar 2010)

Hallo zusammen,

ich habe ein Programm, das über einen Timer einen TimerTask aufruft. TimerTask fordert ja eine run() Methode mit Rückgabetyp void. Nun will ich, das in der main Methode, erst auf das Ende des TimerTask gewartet wird, bevor das Programm weiterläuft? Ich wollte soetwas jetzt mit einer boolean oder ähnlichem lösen, scheitere aber am fehlenden Rückgabetyp?

Vielleicht hat jemand eine Idee?

Viele Grüße


----------



## Thomas Darimont (9. Februar 2010)

Hallo,

musst du unbedingt einen TimerTask verwenden? 

Seit java 5 gibts dafür den ExecutorService bzw. ScheduledExecutorService.

Wenn du nach einer gewissen Zeit eine Aktion (auch wiederholt) ausführen und auf das Ergebnis dieser Aktion warten möchtest kannst du folgendes machen:

```
package de.tutorials;

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import static java.util.concurrent.TimeUnit.SECONDS;;

public class TimedExecutionExample {

  /**
   * @param args
   */
  public static void main(String[] args) throws Exception {
    Callable<Long> callable = new Callable<Long>() {
      @Override
      public Long call() throws Exception {
        
        SECONDS.sleep(10);
        
        return System.currentTimeMillis();
      }
    };
    
    

    System.out.println("Schedule task");    
    ScheduledFuture<Long> scheduledFuture = Executors.newScheduledThreadPool(1).schedule(callable, 5L, SECONDS);

    System.out.println("waiting for completion...");
    
    System.out.println(scheduledFuture.get());
    
    System.out.println("done");
  }

}
```

Oder auch ein wenig einfacher:

```
package de.tutorials;

import static java.util.concurrent.TimeUnit.SECONDS;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TimedExecutionExample {

  /**
   * @param args
   */
  public static void main(String[] args) throws Exception {
    Callable<Long> callable = new Callable<Long>() {
      @Override
      public Long call() throws Exception {

        SECONDS.sleep(10);

        return System.currentTimeMillis();
      }
    };

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Future<Long> future = executorService.submit(callable);
    System.out.println("waiting for completion...");
    System.out.println(future.get());
    System.out.println("done");
    executorService.shutdownNow();
  }

}
```

Gruß Tom


----------



## SaschaB (9. Februar 2010)

Hey,

danke für die fixe Antwort, Nein muss nicht unbedingt ein TimerTask sein, die Aktion soll eben in gewissen Zeitabständen wiederholt werden, genauergesagt versuche ich damit einen Wasserfluss in einem ArrayModell mit Bildern zu realisieren.

Zugegeben, ich blick bei deinem Beispiel noch nicht so ganz durch, werde es aber mal praktisch testen, vielleicht klappt es so ja!

Viele Grüße


----------



## SaschaB (11. Februar 2010)

So haben mir das ganze mal angesehen. Mit meinem Timer löse ich es ja im Moment folgendermaßen:

Meine Methoden werden über run() aufgefrufen, dem timer.schedule übergebe ich dann noch die Wartezeit bis zum Start und die Intervallzeit. Wo kann ich denn in deinem Beispiel die Intervallzeit und die Wartezeit steuern?
Wäre super wenn du es kurz umreißen könntest. 

EDIT: Okay habe gesehen die Zeiten kann ich eigentlich genauso benutzen.
Kann ich im Task irgendwie bestimmt das die Auführungen stoppen soll? Beim Timer habe ich einfach den Timer mit in der Klasse gehabt, dann habe ich ihn dort gestoppt. Oder ich packe jetzt den ExecutorService mit in die Klasse...

Viele Dank schonmal!


----------



## SaschaB (17. Februar 2010)

Hallo zusammen,

ich hoffe dieser Thread ist noch zu finden. Ich habe mich jetzt mit dem  Executor beschäftigt.
Dieser regelt für mich in einem Spiel jetzt das Zeitgesteuerte Ausführen einer Aktion.
Mein Problem: 
Es sollte einen Neustarttaste geben, bei dieser will ich quasi den Executor erneut aufrufen. Das glaub auch, d.h. die Aktion wird ausgeführt, aber sobald ich mittels 
ScheduledFuture<?> fut und dann fut.get auf den Thread ein 2. Mal warten will, hängt sich das Programm auf, ich nehme an ich habe einen groben Denkfehler in diesem Konzept, weiß aber gerade nicht weiter?

Viele Grüße


----------



## Thomas Darimont (18. Februar 2010)

Hallo,

ich verstehe noch nicht ganz was du da vorhast. Beschreib dein Szenario doch mal ein wenig genauer.
Möchtest du den periodischen Ablauf einfach für eine gewisse Zeit anhalten?

Gruß Tom


----------



## SaschaB (18. Februar 2010)

Gerne:

Es geht um das Spiel Plumber (http://www.funny-games.biz/the-plumber.html)

Dort soll nach einer vorgegebenen Zeit das Wasser anfangen zu laufen. Mein Anfangsproblem war, das ich keine Möglichkeit hatte festzustellen, wann das Wasser das Ziel oder eine Wand erreicht hat.
Das konnte ich jetzt mit deinem Tipp überden Rückgabetyp des Callabel lösen. 
Der Aufruf future.get liefert mir ja dann das Ergebnis. Mein Problem war jetzt nur, das ich auch eine Neustartmethode implementieren soll, die dann logischerweiße alles zurücksetzen soll.
In dieser Methode habe ich dann versucht den Thread mittels shutdownNow(); herunterzufahren, ich muss dann aber trotzdem noch auf die Funktion warten, da sie ja nicht gestoppt wird. Sondern der Thread erst nach Ende der rekursiven Funktion stoppt.
Um die Zeitversetzung einzubauen, habe ich in der Rekursion ein sleep gesetzt, dieses wirft mir dann eine InterruptedException um die Ohren, sobald ich sie aufwecke. Lange Rede kurzer Sinn, hier ein gekürzter Codeauschnitt:


```
public class WaterStreamTask implements Callable<Boolean> {

	private Plumber actualLevel;
	private Controller control;

	public WaterStreamTask(Plumber actualLevel, Controller control) {
		this.actualLevel = actualLevel;
		this.control = control;	
	}

	@Override
	public Boolean call() {

		actualLevel.setFinish(waterStream(actualLevel.getStartPositionX(),
				actualLevel.getStartPositionY(), actualLevel.getActualExit()));

		return actualLevel.isFinish();
	}

	/**
	 * 
	 * @param actualLevel
	 * @return
	 */
	public boolean waterStream(int x, int y, int entryOrExit) {

		// get the localToken
		Token localToken = actualLevel.getGameArea()[y][x];
		int newDirection = -1;
		int entry = -1;
		int exit = -1;

		// check if the Token is filled
		localToken.setFilled(true);
		control.redraw();

		// finish reached
		if (localToken instanceof EndToken) {
			actualLevel.setFinish(true);
			return true;
		}

		if (entryOrExit == 0) {
			Token nextToken = actualLevel.getGameArea()[y - 1][x];
			entry = nextToken.getEntry();
			exit = nextToken.getExit();

			if (entry == 2) {
				newDirection = exit;
			} else {
				if (exit == 2) {
					newDirection = entry;
				} else {
					return false;
				}
			}

		}

		if (entryOrExit == 1) {
			Token nextToken = actualLevel.getGameArea()[y][x + 1];
			entry = nextToken.getEntry();
			exit = nextToken.getExit();

			if (entry == 3) {
				newDirection = exit;
			} else {
				if (exit == 3) {
					newDirection = entry;
				} else {
					return false;
				}
			}

		}

		if (entryOrExit == 2) {
			Token nextToken = actualLevel.getGameArea()[y + 1][x];
			entry = nextToken.getEntry();
			exit = nextToken.getExit();

			if (entry == 0) {
				newDirection = exit;
			} else {
				if (exit == 0) {
					newDirection = entry;
				} else {
					return false;
				}
			}

		}

		if (entryOrExit == 3) {
			Token nextToken = actualLevel.getGameArea()[y][x - 1];
			entry = nextToken.getEntry();
			exit = nextToken.getExit();

			if (entry == 1) {
				newDirection = exit;
			} else {
				if (exit == 1) {
					newDirection = entry;
				} else {
					return false;
				}
			}
		}

		
		
		
		try {
			MILLISECONDS.sleep(actualLevel.getTimeBetweenWater());
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
                 ? Hatte versucht hier über ein return false die Funktion zu beenden, das klappt aber nicht so gut
		}

		if (entryOrExit == 0) {
			if (y > 0) {
				return waterStream(x, y - 1, newDirection);
			} else {
				return false;
			}

		} else {
			if (entryOrExit == 1) {
				if (x < actualLevel.getWidth() - 1) {
					return waterStream(x + 1, y, newDirection);
				} else {
					return false;
				}
			} else {
				if (entryOrExit == 2) {
					if (y < actualLevel.getHeight() - 1) {
						return waterStream(x, y + 1, newDirection);
					} else {
						return false;
					}
				} else {
					if (entryOrExit == 3) {
						if (x > 0) {
							return waterStream(x - 1, y, newDirection);
						} else {
							return false;
						}
					}
				}
			}
		}
		return false;
	}
	

}
```

Hier der Aufruf in meiner Mainmethode:

```
ScheduledExecutorService scheduler = Executors
				.newScheduledThreadPool(1);
		ScheduledFuture<?> fut = scheduler.schedule(new WaterStreamTask(
				plumber,this), 1, TimeUnit.MILLISECONDS);
		try {
			fut.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
```

Beim ersten Aufruf hat das gut geklappt, wenn ich ehrlich bin gibt es sogar jetzt auch beim ersten Durchlauf Probleme, aber spätestens beim erneuten Aufruf, aufgrund des Neustarts bewirkt das fut.get() einen freeze, sodass das Fenster und das Programm nicht mehr reagieren. Ich möchte das er wie ein Tiemr quasi wieder von vorne beginnt mit seinem Ablauf...


----------



## Thomas Darimont (18. Februar 2010)

Hallo,

solche Spiele kann man viel einfacher Strukturieren... schau mal hier:
http://www.tutorials.de/forum/java/251899-angehensweise-fuer-ein-brettspiel-mit-java-2d.html

Für dein Beispiel (Pause und Restart):

```
package de.tutorials;

import static java.awt.event.KeyEvent.VK_DOWN;
import static java.awt.event.KeyEvent.VK_LEFT;
import static java.awt.event.KeyEvent.VK_P;
import static java.awt.event.KeyEvent.VK_Q;
import static java.awt.event.KeyEvent.VK_R;
import static java.awt.event.KeyEvent.VK_RIGHT;
import static java.awt.event.KeyEvent.VK_UP;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;

public class WaterMarkExample extends JFrame {

	BufferStrategy bufferStrategy;

	volatile int waterMark;

	volatile boolean pause;
	volatile boolean quit;
	volatile boolean restart;

	volatile boolean upPressed;
	volatile boolean downPressed;
	volatile boolean leftPressed;
	volatile boolean rightPressed;

	volatile Point playerXY = new Point(0, 0);

	Runnable gameLoop = new Runnable() {
		public void run() {
			while (!quit) {
				if (!pause) {
					processInput();
					updateGameState();
				}
				
				updateGraphics();

				try {
					TimeUnit.MILLISECONDS.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			System.out.println("Bye bye");
			setVisible(false);
			System.exit(0);
		}
	};

	private void processInput() {
		int dx = 0;
		int dy = 0;

		int stepSize = 5;

		if (upPressed) {
			dy -= stepSize;
		}
		if (downPressed) {
			dy += stepSize;
		}
		if (leftPressed) {
			dx -= stepSize;
		}
		if (rightPressed) {
			dx += stepSize;
		}
		
		playerXY.translate(dx, dy);
	}

	protected void updateGraphics() {
		Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();

		g.clearRect(0, 0, 400, 300);

		drawWater(g);
		drawPlayer(g);

		g.dispose();
		bufferStrategy.show();
	}

	private void drawPlayer(Graphics2D g) {
		g.setColor(Color.RED);
		g.fillRect(playerXY.x, playerXY.y, 40, 40);
	}

	private void drawWater(Graphics2D g) {
		g.setColor(Color.BLUE);
		g.fillRect(0, 300 - waterMark, 400, waterMark);
	}

	protected void updateGameState() {
		waterMark++;

		if (restart) {
			waterMark = 0;
			restart = false;
		}
	}

	public WaterMarkExample() {
		super("WaterMarkExample");
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		setSize(400, 300);

		setVisible(true);

		createBufferStrategy(2);

		addKeyListener(createKeyListener());

		this.bufferStrategy = getBufferStrategy();

		ExecutorService service = Executors.newSingleThreadExecutor();
		service.execute(gameLoop);
	}

	private KeyListener createKeyListener() {
		return new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				processKeys(e, true);
			}

			public void keyReleased(KeyEvent e) {
				switch (e.getKeyCode()) {
				case VK_P:
					if (pause) {
						pause = false;
					} else {
						pause = true;
					}
					break;
				case VK_Q:
					quit = true;
					break;
				case VK_R:
					restart = true;
					break;
				}

				processKeys(e, false);
			}

			private void processKeys(KeyEvent e, boolean pressed) {
				switch (e.getKeyCode()) {
				case VK_UP:
					upPressed = pressed;
					break;
				case VK_DOWN:
					downPressed = pressed;
					break;
				case VK_LEFT:
					leftPressed = pressed;
					break;
				case VK_RIGHT:
					rightPressed = pressed;
					break;
				}
			}

		};
	}

	public static void main(String[] args) {
		new WaterMarkExample();
	}

}
```

Gruß Tom


----------



## SaschaB (18. Februar 2010)

Danke für die schnelle Antwort. Ich habe mich mal an dem Schlüsselwort volatile versucht. 
Wenn ich jetzt nach meinem Timeraufruf soetwas wie: 


```
while (!timerFinish){
}
```

verwende, bleibt mein Programm aufgrund der stänig wiederholenden Schleife stehen....
Bei einem Sleep in der Schleife das gleiche, ich weiß einfach nicht mehr vor noch zurück... Jeder Versuch auf die Änderung einer Variablen zu warten endet im Freeze, ich habe langsam das Gefühl, das das Problem ganz woanders liegt...


----------

