Prog. 2 9 • Prog. 2 Threads 9 Ein Thread • Programm / Prozess – ist ein eigenständiges Programmstück, das parallel zu anderen Threads laufen kann Threads in Java Threads – sind direkter Bestandteil der Sprache Java A – kann mit einem Prozeß verglichen werden, läuft jedoch auf einer feineren Stufe ab – sind immer Instanzen einer Klasse, die das Interface Runnable implementiert Ein Thread • Runnable deklariert nur die (abstrakte) Methode void run() • Ein Prozeß ist ein Instrument, um ein komplettes Programm auszuführen • run() enthält alle im Thread auszuführenden Anweisungen • Instanzen von Klassen, die Runnable implementieren, heißen (Thread)-Targets • Innerhalb eines Prozesses können mehrere Threads parallel ablaufen • – kommunizieren durch • Zugriff auf Instanz- oder Klassenvariablen Alle Threads eines Programms Programm / Prozess • Aufruf von Methoden, die innerhalb von run() sichtbar sind – teilen sich einen gemeinsamen Adressraum • Adressräume von Prozessen sind streng getrennt A B Prog. 2 9 • • Thread implementiert das Interface Runnable FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke • Jede Instanz von Thread (oder einer von Thread abgeleiteten Klasse) ist Target eines Threads (kann als Thread ausgeführt werden) 1 Die Klasse Thread 9 Konstruktoren • Generischer Name Thread-<nummer> – public Thread(String name) – public Thread(Runnable target, String name) Thread-Target – void run() Enthält auszuführenden Programmcode – void start() throws IllegalThreadStateException Wenn Thread schon gestartet true wenn Thread gestartet wurde – finale boolean isAlive() Gestartet wird ein Thread durch den Aufruf von start() – Anschließend wird die weitere Ausführung automatisch an die Methode run übertragen • Würde wie ein normaler Methodenaufruf aus dem laufenden Thread behandelt – final void setName() final String getName() – final void setPriority(int prio) final int getPriority() FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke Zum Erzeugen eines Threads muss – Direkter Aufruf von run würde keinen neuen Thread erzeugen – static void sleep(long millis) 3 2 Erzeugen eines neuen Threads – die Methode run() überschrieben werden • Methoden FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke – eine Klassen von Thread abgeleitet werden Thread-Name – public Thread(Runnable target) 18.06.2009 18.06.2009 Prog. 2 – public Thread() • C Mehrere Threads – können auf gleiche Variablen zugreifen 18.06.2009 – werden gesteuert durch die Klasse Thread (aus java.lang.Thread) 18.06.2009 public class SimpleThread { public static void main(String[] args) { MyThread thread = new MyThread(); Out.println("Starting thread..."); thread.start(); try { Thread.sleep(20); } catch (InterruptedException e) {} thread.stop(); // Deprecated!!! Out.println("Thread has been stopped!"); } } class MyThread extends Thread { public void run() { int counter=0; while(true) { Out.println(getName()+“:“+counter++); } } }Programm-Ausgabe Starting thread... Thread-0: 1 Thread-0: 2 . . Thread-0: 321Thread has been stopped! FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 4 Prog. 2 9 • Prog. 2 Beenden eines Threads class MyStopThread extends Thread { Ein Thread sollte nicht mit der Methode stop() beendet werden } //cancel = true; thread.myStop(); Out.println("Thread has been stoped"); Out.print("Thread is alive: “); public void run() { Thread thisThread=Thread.currentThread(); • Beendet die Methode run(), wenn die stopMyThread gleich true ist int counter = 0; – Damit sofort auf stop reagiert wird, muss while (thread == thisThread) { // while (!cancel) { • entweder die Variable volatile sein, oder Out.print(currentThread()+": “); Mit dem Schlüsselwort volatile werden Variablen gekennzeichnet, die asynchron (außerhalb des aktuellen Threads) modifizierbar sein sollen. Out.println(counter++); } } • der Zugriff auf die Variable synchronisiert sein. } 5 18.06.2009 Out.println(thread.isAlive()); } } Programm-Ausgabe (Beispiel) Starting thread... Thread[Thread-0,5,main]: 1 . . Thread[Thread-0,5,main]: 59 Thread has been stoped Thread is alive:true Thread[Thread-0,5,main]: 60 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 6 Prog. 2 Unterbrechungsanforderungen 9 Beispiel (interrupt und isInterrupted) public class ThreadInterrupt { public static void main(String[] args) { MyInterruptThread thread = new MyInterruptThread(); System.out.println("Starting thread..."); thread.start(); try { Thread.sleep(100); } catch (InterruptedException e) {} System.out.println("isAlive: “+thread.isAlive()); thread.interrupt(); System.out.println("isAlive: “+thread.isAlive()); } Abbruchflag wird zurückgesetzt } class MyInterruptThread extends Thread { Programm-Ausgabe public void run() { Starting thread... int counter = 0; 0 while (true) { 1 if (isInterrupted()) break; 2 try { Thread.sleep(10); } 3 4 catch (InterruptedException e) { 5 System.out.println(e); isAlive: true interrupt(); 6 } Java.lang.InterruptedException: sleep interrupted 7 System.out.println(counter++); isAlive: false } Das richtige Mass für die Abfragehäufigkeit des „Stop-Flags“ ist in der Praxis schwierig zu finden – Wird „Stop-Flag“ zu oft abgefragt, werden unnötig Ressourcen verbraucht – Wird „Stop-Flag“ zu selten abgefragt, ist die Zeit bis zum Stop zu lang • Thread.sleep(20); } catch (InterruptedException e) { } • überprüft die Variable stopMyThread regelmässig • try { thread = null; – Target Thread 9 thread.start(); public void myStop() { – Variable z.B. boolean stopMyThread zeigt an, ob Thread stoppen soll Prog. 2 Out.println("Starting thread..."); überschrieben werden da sie final ist */ http://java.sun.com/docs/books/tutorial/essential/threads/ FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke MyStopThread thread = new MyStopThread(); /* Die Methode stop() kann nicht http://java.sun.com/j2se/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html 18.06.2009 public static void main(String[] args) { //private volatile boolean cancel = false; Threads am besten durch Verwendung eines Flags beenden – public class MyStopThreadExample { private volatile Thread thread = this; – Es ist nicht definiert und auch nicht voraussagbar, wann der Thread beendet wird • Beispiel 9 Die Klasse Thread stellt hierfür folgende Methoden zur Verfügung – public void interrupt() • Setzt ein (Abbruch-)Flag, das eine Unterbrechungsanforderung signalisiert – public boolean isInterrupted() • Liefert true, wenn das Abbruch-Flag gesetzt ist – public static boolean interrupted() • Liefert den Status des Abbruch-Flags beim aktuellen Thread • Entspricht dem Aufruf von currentThread().isInterrupted() und anschließendem Zurücksetzen des Abbruchflags auf initialen Wert false } 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 7 18.06.2009 } FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 8 Prog. 2 9 • Prog. 2 Die Methode sleep 9 • Mit Hilfe der folgenden Methoden kann ein Thread pausieren Nicht alle Klassen, die als Thread laufen sollen, können von Thread abgeleitet werden – public static void sleep(long millis) – Ist eine Klasse schon Bestandteil einer Vererbungshierarchie, kann sie nicht mehr von Thread abgeleitet werden (keine Mehrfachvererbung in Java) Aktueller Prozeß wird für millis Millisekunden angehalten – public static void sleep(long millis, int nanos) • Aktueller Prozeß wird für millis Millisekunden und nanos Nanosekunden angehalten Anstatt einen Thread durch Ableiten der Klasse Thread, kann man ihn auch durch Implementieren der Interfaces Runnable erzeugen – Das Interface Runnable enthält nur eine einzige Methode Tatsächliche Genauigkeit hängt von der Zielarchitektur ab • public abstract void run() – Unter Windows ist es etwa 1ms • – sleep muss in einem try-catch Block gekapselt werden (kann während der Wartezeit eine Ausnahme vom Typ InterruptedException auslösen) Jede Klasse, deren Instanzen als Thread laufen sollen muss das Interface Runnable implementieren (auch Thread selbst) • Um eine Instanz einer Klasse die Runnable implementiert als Thread laufen zu lassen muss man Beim Starten eines Java-Programms wird automatisch ein Thread für die Ausführung des Hauptprogramms angelegt – Ein neues Thread-Objekt erzeugen 18.06.2009 9 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 9 18.06.2009 Prog. 2 9 public class RunnableExample { int counter; class B extends A implements Runnable { B Kann nicht von Thread abgeleitet werden public class CountThread { public static void main(String[] args) { public Count(long maxNumber) { B b = new B(); Count c = new Count(10); this.maxNumber = maxNumber; Thread thread = new Thread(b); 10 Wo ist der Fehler? long maxNumber; Instanz von B soll als Thread laufen t.start(); FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke class Count extends Thread { public static void main(String[] args) { } Thread t = new Thread(c); – Die Methode start() von Thread aufrufen Runnable (Beispiel) class A { C c = new C(); • Konstruktor erhält Thread-Target – Die Methode sleep kann somit auch dazu verwendet werden das Hauptprogramm pausieren zu lassen Prog. 2 Das Interface Runnable c.start(); } new Thread(c).start(); public void run() { System.out.println("Starting thread..."); public void run() { counter = 0; thread.start(); while(true) { if (Thread.interrupted()) { Ruft die run-Methode der Klasse B auf for(long i=0; i<=maxNumber; i++) Count2 c2 = new Count2(10); System.out.println("Int Count: "+i); c2.start(); } new Thread(c2).start(); } } try { break; } Thread.sleep(200); class Count2 implements Runnable { } } catch (InterruptedException e) {} long maxNumber; System.out.println(counter++); thread.interrupt(); public Count2(long maxNumber) { } System.out. println("Thread has been stopped"); } this.maxNumber = maxNumber; } } } Geht nicht da Count2 keine Methode start() kennt public void run() { } for (long i=0; i<=maxNumber; i++) System.out.println("Int Count2: "+i); } } 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 11 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 12 Prog. 2 9 Prog. 2 Synchronisationsprobleme class T1 extends Thread{ 9 • class Person { Person p; String name; public T1(Person p) { String surname; this.p = p; • public void setName(String n, String s) { } name = n; public void run() { surname = s; System.out.println(p.getName()); } } public String getName() { } return name + " " + surname; Synchronisation mittels synchronized Um Threads zu synchronisieren gibt es in Java das Konzept des Monitors Ein Monitor kapselt einen kritischen Bereich (Programmteil, der nur von einem Programmteil auf einmal durchlaufen werden darf) mit Hilfe einer automatischen Sperre Person p; Person p = new Person(); } T1 t1 = new T1(p); public void run() { T2 t2 = new T2(p); t1.start(); } } } Mögliche Programmausgaben FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke Synchronisation von Methoden 9 class Output { int number; static synchronized void printRow(int number){ public static void main(String[] args) { • for (int i = 0; i < 10; i++) { Thread thread1 = new SyncedFuncThread(10); int n = 0; thread1.start(); double w; thread2.start(); } Thread.sleep(2000); • System.out.println(); } catch (InterruptedException e) {} public void run() { while (true) { synchronized(getClass()){ Mehrere Instanzen von SyncedThread System.out.println(counter++); } } } FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 14 Animation Eine Animation ist das aufeinanderfolgende Anzeigen einer Sequenz von Einzelbildern Probleme bei der Animation – Einzelbilder müssen im richtigen Timing angezeigt werden } thread1.stop(); } thread2.stop(); } • Zu wenig Bilder pro Zeiteinheit führen zu „ruckelnder“ Animation Ohne synchronized public SyncedFuncThread(int n) { number = n; } public void run() { for (int i = 0; i < 10; i++) Output.printRow(number); 18.06.2009 thread2.stop(); } while (n < 10000) w = Math.sqrt(n++); try { } thread1.stop(); – Trägheit des menschlichen Auges sorgt für die Illusion einer zusammenhängenden Bewegung System.out.print(number + " "); Thread thread2 = new SyncedFuncThread(20); } Thread.sleep(1000); } catch (InterruptedException e) {} Das Monitor-Konzept wird in Java durch das Schlüsselwort synchronized realisiert 18.06.2009 Prog. 2 public class SyncedFuncThread extends Thread { try { } 13 Prog. 2 9 thread2.start(); – Es kann eine komplette Methode oder ein Block innerhalb einer Methode geschützt werden Peter Barth Wolfgang Weitz Peter Weitz Wolfgang Barth } 18.06.2009 • t2.start(); p.setName("Wolfgang","Weitz"); thread1.start(); – Ist die Sperre beim Eintritt in einen Monitor gesetzt, muss der aktuelle Prozeß warten, bis die Sperre freigegeben wird public static void main(String[] args) { p.setName("Peter","Barth"); Thread thread2 = new SyncedThreads(); • Verlassen zurückgesetzt public class NameSync { this.p = p; Thread thread1 = new SyncedThreads(); • Betreten des Bereichts gesetzt } public T2(Person p) { static int counter = 0; public static void main(String[] args) { – Sperre wird beim } class T2 extends Thread{ public class SyncedThreads extends Thread { 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 10 10 10 10 20 20 10 10 10 20 20 10 10 10 20 20 10 10 10 20 20 10 10 10 20 20 10 10 10 20 20 10 10 20 20 20 20 20 20 20 20 20 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 10 10 10 20 20 20 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke – Darstellung der Animation darf die Interaktivität eines Programms nicht beeinflussen – Einzelbilder müssen mit Hilfe von „Double-Buffering“ oder ähnlichen Techniken gezeichnet werden Mit synchronized 10 10 10 20 20 20 • Sollen zu viele Bilder gezeichnet werden, können Bildteile „verloren“ gehen • Naive Verwendung der paint/repaint-Methoden von Java führt zu starkem Flackern 15 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 16 Prog. 2 9 • Prog. 2 Animation in Java 9 Einfach Animation (ohne eigenen Thread) import java.awt.*; Eine Animation kann in Java dadurch realisiert werden, dass in einer Schleife die Methode repaint wiederholt aufgerufen wird public void startAnimation() { import java.awt.event.*; while (true) { public class SimpleAnim extends Frame { } repaint(); – Aufruf von repaint führt zum internen Aufruf der paint-Methode int count = 0; } – paint erzeugt dann das jeweils aktuelle Einzelbild public static void main(String[] args) { public void paint(Graphics g) { SimpleAnim window = new SimpleAnim(); • Die Methode paint muss wissen, welches Bild bei welchem Aufruf erzeugt werden soll count = (count + 10) % getWidth(); window.setBounds(100, 100, 400, 400); g.fillRect(count, 175, 50, 50); window.setVisible(true); try { // 25 frames per second (fps) window.startAnimation(); – Meist wird hierzu ein Schleifenzähler verwendet, der das aktuelle Bild bezeichnet Thread.sleep(40); } } catch (InterruptedException e) {} } public SimpleAnim() { – Nach der Ausführung von paint wartet der Aufrufer eine gewisse Zeitspanne super("Simple animation"); } setBackground(Color.BLUE); addWindowListener(new WindowAdapter() – Anschließend wird der Schleifenzähler erhöht und erneut paint aufgerufen { public void windowClosing(WindowEvent e) { – Das ganze wird solange wiederholt, bis die Animation beendet ist System.exit(0); } }); } 18.06.2009 Prog. 2 9 • FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 17 Verwendung von Threads 9 Damit Programme, die Animationen enthalten, durch diese nicht komplett lahm gelegt werden, sollten Haupt-Thread eines Animations-Programms hat dann genug Zeit, um – die Bildschirmausgabe durchzuführen – weitere Events zu bearbeiten Zur Verwendung mehrerer Threads muß die Fensterklasse – das Interface Runnable implementieren • Die Methode run() überschreiben – eine Instanzvariable vom Typ Thread anlegen 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 18 Einfach Animation (in eigenem Thread) import java.awt.*; import java.awt.event.*; public void startAnimation() { public class AnimWThread extends Frame implements Runnable { int x = 100, y = 10; int dx = 10, dy = 10; public static void main(String[] args) { AnimWThread window = new AnimWThread(); window.setBounds(100, 100, 400, 400); window.setVisible(true); window.startAnimation(); } } Thread thread = new Thread(this); thread.start(); – Zeitverzögerungen in die repaint-Schleifen verlegt werden • FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke Prog. 2 – alle repaint-Schleifen in eigenen Threads laufen • 18.06.2009 19 public void run() { while (true) { repaint(); try { // 25 fps Thread.sleep(40); } catch (InterruptedException e) {} } } public void paint(Graphics g) { public AnimWThread() { x +=dx; super("Simple animation with thread"); if(x>getWidth()-50 || x<0) dx = -dx; setBackground(Color.YELLOW); y +=dy; addWindowListener(new WindowAdapter() { if(y>getHeight()-50 || y<0) dy = -dy; public void g.fillRect(x, y, 50, 50); windowClosing(WindowEvent e) { } System.exit(0); } } }); } 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 20 Prog. 2 9 • • Reduzierung des Bildschirmflackerns 1. Bildschirm nicht löschen import java.awt.*; import java.awt.event.*; – Es erscheint somit kurz ein vollständig leerer Hintergrund – Dies führt zu mehr oder weniger starkem „Flackern“ der Animation public class AnimWThread extends Frame implements Runnable { int x = 100, y = 10; int dx = 10, dy = 10; public static void main(String[] args) { AnimWThread window = new AnimWThread(); window.setBounds(100, 100, 400, 400); window.setVisible(true); window.startAnimation(); } Bildschirmflackern kann unterschiedlich unterdrückt werden – Aufruf von repaint ruft zunächst die Methode update der Klasse Component public void update(Graphics g) { Ersetzen durch g.setColor(getBackground()); g.fillRect(0,0,width,height); g.setColor(getForeground()); paint(g); public void update(Graphics g) { paint(g); } } 2. Nur den wirklich benötigen Bildschirmteil löschen und neu zeichnen 3. Double-Buffering (im Hintergrund zeichnen, fertige Zeichnung anzeigen) 18.06.2009 9 9 Vor jedem Aufruf von paint wird zunächst der gesamte Inhalt des Fensters gelöscht 1. Bildschirm nicht löschen (nur bei nicht bewegten Animationen möglich) Prog. 2 Prog. 2 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 21 public void startAnimation() { Thread thread = new Thread(this); thread.start(); } public void run() { while (true) { repaint(); try { // 25 fps Thread.sleep(40); } catch (InterruptedException e) {} } } public void paint(Graphics g) { x +=dx; public AnimWThread() { if(x>getWidth()-50 || x<0) dx = -dx; super("Simple animation with thread “+ y +=dy; “(without clearing the frame)“); if(y>getHeight()-50 || y<0) dy = -dy; setBackground(Color.YELLOW); g.fillRect(x, y, 50, 50); addWindowListener(new WindowAdapter() } { public void public void update(Graphics g) { windowClosing(WindowEvent e) { paint(g); System.exit(0); } } } 18.06.2009}); FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 22 } Prog. 2 1. Bildschirm nicht löschen 9 • 2. Nur den benötigten Bereich löschen Bei bewegten Animationen kann es sinnvoll sein, nur die Teile des Bildschirms zu löschen, die – im aktuellen Animationsschritt leer sind – im vorangegangenen Schritt Grafikelemente enthielten • Hierzu wird wiederum das automatische Löschen des Bildschirms durch Überlagern der Methode update verhindert • In jedem Animationsschritt müssen alle benötigten Informationen über den vorangegangenen Schritt vorhanden sein – Die Applikation muss sich dann selbst um das Löschen der relevanten Bildbereiche kümmern 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 23 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 24 Prog. 2 9 Prog. 2 2. Nur den benötigten Bereich löschen public void startAnimation() { Thread thread = new Thread(this); thread.start(); } import java.awt.*; import java.awt.event.*; public class AnimWThread extends Frame implements Runnable { int x = 100, y = 10; int dx = 10, dy = 10; public static void main(String[] args) { AnimWThread window = new AnimWThread(); window.setBounds(100, 100, 400, 400); window.setVisible(true); window.startAnimation(); } public AnimWThread() { super("Simple animation with thread “+ “(clear only part of the frame)“); setBackground(Color.YELLOW); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } 18.06.2009 Prog. 2 9 9 • Double-Buffering ist ein universelles Mittel gegen Bildschirmflackern – Bei jedem Animationsschritt werden zunächst alle Bildschirmausgaben in einen Buffer (Offscreen-Image) geschrieben public void run() { while (true) { repaint(); try { // 25 fps Thread.sleep(40); } catch (InterruptedException e) {} } } – Sind alle Ausgaben abgeschlossen, wir das Offscreen-Image auf die Fensteroberfläche kopiert public void paint(Graphics g) { g.setColor(getBackground()); g.fillRect(x, y, 50, 50); x +=dx; if(x>getWidth()-50 || x<0) dx = -dx; y +=dy; if(y>getHeight()-50 || y<0) dy = -dy; g.setColor(Color.BLACK); g.fillRect(x, y, 50, 50); } • } Double-Buffering läßt sich in Java komplett kapseln • Hierzu leitet man von der Klasse Frame eine neue Klasse DoubleBufferingFrame ab Graphics Offscreen-Image Bildschirm 2. Kopieren – Alle Bildschirmausgaben erfolgen auf den Offscreen-Grafikkontext – Sind alle Ausgabeoperationen abgeschlossen, wird das Offscreen-Image mittels drawImage in das Ausgabefenster kopiert 25 18.06.2009 Prog. 2 9 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 26 Eine Klasse DoubleBufferFrame public class DoubleBufferFrame extends Frame { public void update(Graphics g) { private Image dbImage; // Initialising double buffer private Graphics dbGraphics; if (dbImage == null || resized) { private boolean resized; resized = false; dbImage = createImage( public DoubleBufferFrame() { this.getSize().width, addCL(); • dbImage enthält das Offscreen-Image this.getSize().height); } • dbGraphics enthält den Offscreen-Grafikkontext – Zusätzlich muss die Methode update geeignet überschrieben werden dbGraphics = dbImage.getGraphics(); } public DoubleBufferFrame(String s) { // Clear background super(s); • In update wird paint mit dbGraphics als Argument aufgerufen dbGraphics.setColor(getBackground()); addCL(); – paint sendet dann alle Grafikbefehle auf das Offscreen-Image dbGraphics.fillRect(0, 0, } • Zum Schluß wird mittels g.drawImage(dbImage,0,0,this) das OffscreenImage auf dem Bildschirm angezeigt Auf Änderung der Fenstergröße reagieren this.getSize().width, void addCL() { this.getSize().height); addComponentListener( • Bildschirm – Mittels getGraphics erhält man einen Grafikkontext zu dem Image – Die neue Klasse enthält zwei private Membervariable • Offscreen-Image – Ein Fensterobjekt beschafft sich durch Aufruf der Methode createImage ein Offscreen-Image und speichert es in einer Instanzvariablen Erweitern der Klasse Frame • 1. Zeichnen Prinzipelle Funktionsweise des Double-Bufferings in Java public void update(Graphics g) { paint(g); } FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 3. Double-Buffering Alle Klassen, die von DoubleBufferingFrame abgeleitet sind, unterstützen nun das Double-Buffering automatisch // Paint foreground new ComponentAdapter() { dbGraphics.setColor(getForeground()); public void paint(dbGraphics); componentResized(ComponentEvent e) // Show offscreen { Damit können schon bestehende Programme nachträglich flackerfrei gemacht werden, ohne dass eine einzige Zeile der eigentlichen Ausgaberoutinen geändert werden muss g.drawImage(dbImage, 0, 0, this); resized = true; } } }); } } 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 27 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 28 Prog. 2 9 Prog. 2 Animation mit Double-Buffering (Beispiel) import java.awt.*; import java.awt.event.*; 9 Double-Buffering Beispiel public void run() { while (true) { repaint(); public class DoubleBufferExample extends DoubleBufferFrame implements Runnable { int xB = 100, yB = 100, xS = 150, yS = 150; int dx = 10, dy = 10, angle = 0; double rad; try { // 25 fps Thread.sleep(40); } catch (InterruptedException e) {} angle = (angle + 10) % 360; rad = angle*Math.PI/180; public static void main(String[] args) { DBExample window = new DBExample(); window.setBounds(100, 100, 400, 400); window.setVisible(true); window.startAnimation(); } xB += dx; if (xB>getWidth()-70 || xB<0) dx = -dx; yB += dy; if (yB>getHeight()-70 || yB<0) dy = -dy; xS = xB + 15 + (int)(35*Math.sin(rad)); yS = yB + 15 + (int)(35*Math.cos(rad)); public DoubleBufferExample() { super("Double-Buffering example"); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); }}); } public void paint(Graphics g) { public void startAnimation() { Thread thread = new Thread(this); thread.start(); } } 18.06.2009 } } g.setColor(Color.BLUE); g.fillOval(xB, yB, 50, 50); g.setColor(Color.GREEN); g.fillOval(xS, yS, 20, 20); } FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 29 18.06.2009 FH-Wiesbaden --- Medieninformatik --- SS 2009 --- Prof. Dr. Ulrich Schwanecke 30