package javacodebook.thread.metronome; /** * Eine Klasse, die alle x Sekunden eine Nachricht an alle Listener sendet. */ public class Metronome extends java.util.Observable { int period; MetronomeThread thread; boolean isStarted; public Metronome(int period) { this.period = period; this.isStarted = false; } public void start() { if (isStarted) return; thread = new MetronomeThread(this, period); thread.start(); isStarted = true; } public void stop() { if (isStarted == false) return; thread.stopExecution(); isStarted =false; } protected void periodElapsed() { setChanged(); notifyObservers(); } } --- Neue Klasse --- package javacodebook.thread.metronome; class MetronomeThread extends Thread { Metronome metronome; boolean stop; int period; MetronomeThread(Metronome metronome, int period) { this.metronome = metronome; this.stop = false; this.period = period; } void stopExecution() { stop = true; } public void run() { long start = System.currentTimeMillis(); while(!stop) { try { // Schlafen, bis eine Periode um ist. Es kann vorkommen, dass // bei der Benachrichtigung von vielen Observern Zeit verloren // geht. Diese verlorene Zeit wird errechnet und der Thread // entsprechend kürzer schlafen gelegt. long now = System.currentTimeMillis(); long left = (period*1000) - ((now-start)%(period*1000)); // Da die sleep-Methode manchmal etwas zu früh aufwacht, müssen // wir verhinden, dass die Observer in einer Periode zwei mal // benachrichtigt werden. Ein Puffer von 500 ms reicht. if (left < 500) left += (period*1000); sleep(left); metronome.periodElapsed(); } catch (Exception ignore) {} } } } --- Neue Klasse --- package javacodebook.thread.metronome; /** * Diese Testklasse horcht auf Signale der Klasse Metronome und zeigt an, nach * wie viel Millisekunden sie ein Signal aufgefangen hat. */ public class TestListener implements java.util.Observer { long startTime; public TestListener() { startTime = System.currentTimeMillis(); } public void update(java.util.Observable observable, Object obj) { System.out.print("Benachrichtigung nach "); System.out.print(System.currentTimeMillis() - startTime); System.out.println(" Millisekunden"); // Wir simulieren ein bischen Arbeit um zu zeigen, dass sich der Timer // durch langes abarbeiten der Benachrichtigungen nicht beeindrucken // laesst. for (int i=0; i<10000; i++); } } --- Neue Klasse --- package javacodebook.thread.metronome; import java.text.SimpleDateFormat; /** * Erzeugt einen neuen Timer und läßt einen Listener auf TimerEvents horchen. */ public class Starter { public static void main(String []args) { TestListener tl = new TestListener(); Metronome metronome = new Metronome(5); metronome.addObserver(tl); metronome.start(); } }