Verteilte Systeme Neues Kapitel: Threads - oth

Werbung
Neues Kapitel: Threads, Prozesse und GUIs
Verteilte Systeme
Hochschule Regensburg
Vorlesung 3, 11.04.2012
Universitätsstraße 31, 93053 Regensburg
Prof. Dr. Jan Dünnweber
Die Kommunikation (s. vorherige Vorlesung) findet zwischen
Prozessen (Programmen in Ausführung) statt
Prozess als Konzept stammt aus dem Betriebssysteme-Bereich
Inhalt des Kapitels:
◮
◮
◮
Das Konzept von Thread vs. Prozess
Aufbau von Clients und Servern mit Threads
Implementierung von GUIs mit Swing und Servlets
Prof. Dr. Jan Dünnweber, Folie 2 von 27
Prozesse und Threads
Verteilte Systeme
Threads in der nicht-verteilten Welt
Prozess: ein in Ausführung befindliches Programm, mit
seinen Daten, Dateien, Registern, etc.
Auf konventionellen Rechnern (z.B. PCs) arbeiten Prozesse
i. d. R. nebenläufig zueinander auf einer CPU, d. h. sie teilen
den Prozessor unter sich
Das Betriebssystem sorgt für die
Nebenläufigkeits-Transparenz: der Benutzer weiss nichts von
den “fremden” Prozessen
Das Umschalten des Prozessors zwischen den Prozessen ist
aufwendig: der alte Adressraum muß gerettet und der neue
geladen werden – sog. Kontextwechsel
Threads sind Prozessen ähnlich, jedoch können mehrere
Threads unter sich einen Adressraum teilen:
Viele konventionelle Anwendungen laufen schneller mit
mehreren Threads, z. B. ein Thread ist verantwortlich für
Berechnungen und einer für die GUI-Interaktionen
Derartige funktionelle Strukturierung ist häufig auch
softwaretechnisch sinnvoll (vgl. 3-tier architecture und andere
Schichtenmodelle)
Mehrere Threads auf Multiprozessor-Systemen benutzen
verschiedene CPUs einer Maschine, mit Leistungssteigerung
+ Man gewinnt Leistung: Kontextwechsel ist viel schneller
– Man verliert evtl. an Transparenz: der Anwender ist zuständig
für die korrekte, konfliktfreie nebenläufige Ausführung
Prof. Dr. Jan Dünnweber, Folie 3 von 27
Verteilte Systeme
Prof. Dr. Jan Dünnweber, Folie 4 von 27
Verteilte Systeme
Implementierung von Threads
Threads in Java I: Erben von Klasse Thread
Ein Thread-Paket kann zweierlei implementiert werden
Implementierung als Thread-Bibliothek auf Benutzerebene
◮
◮
◮
Einfach/billig Threads zu erzeugen/zerstören
Ein Kontextwechsel ist schnell (wenige Anweisungen)
Ein blockierender Aufruf (z. B. I/O) in einem Thread blockiert
alle anderen Threads des gleichen Prozesses
Implementierung im Kernel des Betriebssystems:
◮
◮
Keine Blockierung, dafür aber ist für jede Thread-Operation
ein Systemaufruf erforderlich
Kontextwechsel fast genauso teuer wie für einen Prozess
Aktuell wird hybride Lösung favorisiert, mit LWP
(Light-Weight Processes): eine Mischung aus Benutzerebene
und BS-Kernel.
Java Threads
Java ist erste universelle Programmiersprache, die Threads direkt
auf Sprachebene unterstützt
Prof. Dr. Jan Dünnweber, Folie 5 von 27
Verteilte Systeme
Implementierung von Java Threads
Wie auch viele andere Konzepte in der Java-Welt, werden Threads über
Klassen bzw. Vererbung zur Verfügung gestellt
Erste Methode der Thread-Deklaration
in einem Java Programm:
Die Java-Klasse Thread beinhaltet
eine (ursprünglich leere) Methode
run()
Die Benutzer-Klasse MyThread wird
als Unterklasse von Thread deklariert
Die (vererbte) Methode run() wird in
MyThread überschrieben
Prof. Dr. Jan Dünnweber, Folie 6 von 27
Threads in Java I: Erben von Klasse Thread (Forts.)
Verteilte Systeme
Threads in Java II: Implementieren von Runnable
Nachteil der ersten Methode: Abgeleitete Klasse
(wie MyThread) darf von keiner anderen Klasse
erben, da Java die Mehrfachvererbung verbietet
class MyThread extends Thread {
public void run() { ... } }
... Thread a = new MyThread();
Thread
run(){}
Zweite Methode der Thread-Deklaration in Java Programmen:
Java-Interface Runnable enthält abstrakte Methode run()
Die Benutzer-Klasse MyRunnable implementiert Runnable,
indem sie die Methode run() definiert
MyThread
run(){...}
Merke: die Java-Klasse Thread implementiert Runnable ebenfalls
Vor- und Nachteile der zweiten Methode:
+ Kann von anderen Klassen erben
Klassendiagramm
in UML-Notation
Unif. Modeling Lang.
Prof. Dr. Jan Dünnweber, Folie 7 von 27
Verteilte Systeme
– Kann Instanzmethoden von Thread nicht direkt benutzen.
Umweg: über die Klassenmethode currentThread() erhält
man die Referenz auf den aktuellen Thread in der
run-Methode
Prof. Dr. Jan Dünnweber, Folie 8 von 27
Verteilte Systeme
Zweite Methode: UML Diagramm und Java-Deklaration
Threads in Java I und II: Erzeugen und Starten
Ein Thread ist immer ein Objekt der Klasse Thread und wird
(wie jedes Java-Objekt) mit new von einem Konstruktor erzeugt:
Runnable
Bei der ersten Methode:
Thread a = new MyThread();
run(){}
Bei der zweiten Methode:
MyRunnable
target
Thread a = new Thread(new MyRunnable()); oder (in der
Klasse MyRunnable): Thread a = new Thread(this);
Thread
run(){...}
Z.B. für ein Panel:
In beiden Fällen kann der Konstruktor ein String-Argument haben
(der Name des Threads, wird zum Debuggen benutzt); sonst
bekommen die Threads ihre Namen vom System in der
Erzeugungsreihenfolge: Thread-1, Thread-2, ...
class MyRunnable extends java.awt.Panel implements Runnable {
public void run() { ... }
}
... Thread a = new Thread(new MyRunnable());
Beachte: Ein erzeugter Thread existiert zwar, läuft aber nicht!
Thread muß in beiden Fällen explizit gestartet werden:
a.start();
was die Ausführung seiner run() Methode veranlaßt.
Prof. Dr. Jan Dünnweber, Folie 9 von 27
Verteilte Systeme
Threads in verteilen Systemen: Client
Threads erlauben blockierende Systemaufrufe, ohne dass der
gesamte Prozess blockiert wird
In einem verteilten System erlaubt dies:
◮
◮
Kommunikation über mehrere logische Verbindungen
Das Verbergen langer Latenzzeiten in Weitverkehrsnetzen
(Latenzen bis in den Sekundenbereich sind üblich)
Beispiel – Web-Browser:
◮
◮
◮
◮
Es wird versucht, mit dem Anzeigen einer HTML-Seite
anzufangen, noch bevor sie voll übertragen wurde
So kommen Textsegmente früher als die Bilder
Multithreaded Browser: es werden separate Threads
aktiviert, jeder richtet eine separate Verbindung zum Server ein
und empfängt die Daten
So ein Multithreaded-Client ist besonders vorteilhaft, wenn der
Webserver über mehrere Maschinen repliziert ist: jeder Thread
kommuniziert mit eigener Replik
Prof. Dr. Jan Dünnweber, Folie 11 von 27
Verteilte Systeme
Prof. Dr. Jan Dünnweber, Folie 10 von 27
Verteilte Systeme
Client: Benutzeroberflächen
Benutzeroberfläche (GUI) – eine der wichtigsten Funktionen
bei einem Client
Manchmal ist die Benutzeroberfläche in die Hardware
integriert, z. B. Display+Tastatur in Handys, evtl.
Spacherkennung, etc.
Die Oberfläche verbirgt vom Benutzer, dass verschiedene
Anwendungsteile von versch. Programmen verarbeitet werden;
ein Teil geändert ⇒ andere werden benachrichtigt
Multithreading ist ein elementarer Bestandteil beinahe jeder
Benutzeroberfäche. Warum? ⇒ a closer look at Java Swing ...
Prof. Dr. Jan Dünnweber, Folie 12 von 27
Verteilte Systeme
Java Swing - Die Welt in der wir uns bewegen
Multithreading & Swing: Einführendes Beispiel
Animation mit interaktiver Kontrolle
Java Maskottchen “Duke”
© by Oracle Corp.
Technologie: Swing, die standard Java Bibliothek
für graphical user interfaces (GUIs)
Swing GUIs sind plattformunabhängig
Prinzip: Ableiten von Basis-Komponenten (Widgets)
& Überschreiben anwendungsspezifischer Details
Start/Stop-Button:
Würfel drehen oder
anhalten
Reset-Button:
Würfel auf
Ausgangsposition
Schieberegler:
Geschwindigkeit
oder Drehrichtung
ändern
Alternativen: Qt/Gnome (Linux), Motif (Solaris),
Aqua/Cocoa & Quartz (Mac), MFC (Windows), AWT
© Oracle
Swing & Multithreading:
Warum programmiert man
GUIs multi-threaded und was
ist dabei zu beachten?
1
Responsivität: GUI darf nicht blockieren
2
Ereignisverarbeitung: GUIs sind implizit multithreaded
3
Thread-Sicherheit: Threads dürfen sich gegenseitig nicht
behindern ⇒ Hierfür ist der Programmierer verantwortlich
Prof. Dr. Jan Dünnweber, Folie 13 von 27
Verteilte Systeme
Prof. Dr. Jan Dünnweber, Folie 14 von 27
... zurück zum Beispiel: Ein Blick auf die Architektur
Beim Design der Beispielanwendung wurde das Architektur-Pattern
Model-View-Controller umgesetzt
Direkte Referenzen:
Event-Dispatching
Main ThreadThread
Controller→Model,
Controller→View und
Controller
HochschuleMain.java
View→Model.
r
e
en
Indirekter
Zugriff von
st d()
i
e
L
ge ng
an ha
Model→View und
Ch etC
s
JPanel
View→Controller.
repaint()
View
Model
Dashboard.java
RedCube.java
Multithreading in
allen Komponenten
User Thread
User Thread Thread
Event-Dispatching
Ein- und Ausgabe-Logik im
Event Dispatching Thread
Vorteil: Austauschbare Kompenenten, z. B. verschiedene Views und
Controller in Swing- und Web-Oberfläche für das gleiche Model
Darstellung (User Thread) läuft unabhängig von der
Verarbeitung von Programm- und Systeminteraktionen
Prof. Dr. Jan Dünnweber, Folie 15 von 27
Verteilte Systeme
Nur bei paralleler Verarbeitung von Programmund Benutzeraktivität gelingt das Zusammenspiel
Daher im Folgenden intensive Architekturund Code-Analyse (7 slides of code ...)
Verteilte Systeme
deep dive into the code: Wie funktioniert sowas?
Der Konstruktor der Controller-Klasse (HochschuleMain)
initialisiert View (Dashboard) & Model (RedCube):
public class HochschuleMain
implements ActionListener, ChangeListener {
public static final int WIDTH = 360, HEIGHT...;
public static final String TITLE ="HS München";
private int width, height, inset;
private Dashboard controls;
private RedCube cube;
public HochschuleMain(Container content) {
this.inset = INSET;this.width = WIDTH; ...
cube = new RedCube(width, height);
controls = //this = ActionListener & ChangeListener
new Dashboard( content, cube,
this, this );
controls.setup( ); } ...
Prof. Dr. Jan Dünnweber, Folie 16 von 27
Verteilte Systeme
(1/7)
Controller continued: Implizites Multithreading
(2/7)
actionPerformed und stateChanged Schnittstellen-Methoden
zur Verarbeitung von Eingaben im Event Dispatching Thread:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run( ) {
JFrame frame = new JFrame(TITLE);
Container content = frame.getContentPane( );
HochschuleMain rotation = new HochschuleMain(content);
frame.setSize( ... ); ...
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE );
...
new Thread(rotation.cube).start();
rotation.cube.stopRotation( );// start resting ...
frame.setVisible(true); } }); }
public void stateChanged(ChangeEvent slide) {
cube.setVelocity(controls.getVelocity( )); }
Verteilte Systeme
View-Komponente Dashboard: Widget Layout
Komponenten-Setup, inklusive Hinzufügen der Listener:
public class Dashboard extends GridBagLayout {
final static String START = "Start", ...; // const
private JSlider velocity; ... // member variables
public Dashboard(Container c, RedCube cube,
ActionListener buttons, ChangeListener adjuster) {
this.content = c; this.cube = cube;
velocity = new JSlider(..); start = new JButton(..);
constraints = new GridBagConstraints( );
start.addActionListener(buttons);
... velocity.addChangeListener(adjuster); ... }
public void setup() {
content.setLayout(this);
constraints.fill = GridBagConstraints.BOTH; ...
...content.add(cube, constraints);...}...
Prof. Dr. Jan Dünnweber, Folie 19 von 27
Verteilte Systeme
(3/7)
Die invokeLater-Methode der SwingUtilities macht das Setup
explizit zu einer Aufgabe des Event Dispatching Threads:
public void actionPerformed(ActionEvent push) {
JButton button = (JButton)pushed.getSource( );
String label = button.getText( );
if (label.equals(Dashboard.START)) {
button.setText(Dashboard.STOP);
cube.resumeRotation( );
} else if (label.equals(Dashboard.STOP)) {
button.setText(Dashboard.START);
cube.stopRotation( );
} else if (label.equals(Dashboard.RESET)) {
cube.reset( ); } else System.exit(0); }
Prof. Dr. Jan Dünnweber, Folie 17 von 27
Hauptprogramm: Die main-Methode
Prof. Dr. Jan Dünnweber, Folie 18 von 27
(4/7)
Verteilte Systeme
Business Logic: Das Model, RedCube in Java
(5/7)
Logo laden, Kachel mit dem Würfel mittels Advanced Imaging API
(JAI) ausschneiden und für die Rotation vorbereiten:
import javax.media.jai.JAI; // ...
class RedCube extends JPanel implements Runnable {
final static int DEFAULT_ANGLE = −22, ...; // const
private int width, height, inset, angle...;// members
public RedCube(int width, int height) {
velocity = DEFAULT_VELOCITY; ...
paused = new ReentrantLock( );
// deadlock−save
running = paused.newCondition( ); // thread control
... b = ImageIO.read(new File(SOURCE_FILE)); ...
TiledImage cubeData = // extract cube data
source.getSubImage(cubeOrigin, ... cubeSize);
cubeSettings = new ParameterBlockJAI("rotate");
... cubeSettings.addSource(cubeData);
setDoubleBuffered(true); }
Prof. Dr. Jan Dünnweber, Folie 20 von 27
Verteilte Systeme
Parallele Berechnung und Darstellung
(6/7)
Die Methode paintComponent läuft im Event Dispatching Thread
während die run-Methode explizit im User Thread läuft:
public void paintComponent(Graphics handle) {
handle.drawImage( background, ... );
cubeSettings.setParameter("angle", ... angle));
RenderedOp op = JAI.create("rotate", cubeSettings);
cube = op.getRendering( ).getAsBufferedImage( );
... handle.drawImage(cube ... cubeSize); }
public void run( ) {
for ( ; ; ) { while (suspended) {
paused.lock( );
running.awaitUninterruptibly( );
paused.unlock( ); }
rotate(angle = angle + direction % 360);
try { Thread.sleep(20 − velocity);
} catch (InterruptedException e) { }
} } ...
Prof. Dr. Jan Dünnweber, Folie 21 von 27
Verteilte Systeme
Was haben wir aus dem Quelltext des Beispiels gelernt?
GUIs erfordern Programmiermethodik, die das Multithreading
berücksichtigt: Komponenten sind i. A. nicht thread-safe!
Grundregel für Swing: Generieren und manipulieren Sie alles
ausschließlich innerhalb des Event Dispatching Thread!
noch einfacher: Verwenden Sie niemals getGraphics!
Innerhalb des Event Dispatching Thread laufen:
1 Die CallBack-Methoden von JComponent und abgeleiteten
Klassen: paintComponent, paintBorder & paintChildren
2 Aktivitäten, die Sie mittels SwingUtilities steuern:
invokeAndWait(Runnable activity): Synchron mit
dem aufrufenden Thread, d.h. Folgeanweisungen warten
invokeLater(Runnable activity): Asynchron, d. h.
der aufrufende Thread arbeitet nebenläufig weiter
Aktivieren Sie den Event Dispatching Thread mittels repaint
(Ausnahme: bei heavyweight Komponenten → update verwenden)
Prof. Dr. Jan Dünnweber, Folie 23 von 27
Verteilte Systeme
... und schließlich: Thread-Kontrolle im RedCube
public void setVelocity(int velocity) {
if (velocity >= DEFAULT_VELOCITY) {
this.direction = DEFAULT_DIRECTION;
this.velocity = velocity; }
else {
this.direction = −DEFAULT_DIRECTION;
this.velocity = MAX_VELOCITY − velocity;
public void rotate(int angle) {
this.angle = angle; repaint( );
(7/7)
} }
}
public void stopRotation( ) {
paused.lock( ); suspended = true;
paused.unlock( ); }
public void resumeRotation( ) {
paused.lock( );
suspended = false; running.signal( );
paused.unlock( ); }
Prof. Dr. Jan Dünnweber, Folie 22 von 27
Verteilte Systeme
AWT & Swing, heavyweight vs. lightweight
Alle Swing Komponenten (JFrame, JPanel, ...) haben einen
heavyweight-Ancestor im Abstract Window Toolkit (AWT)
AWT-Komponenten (Frame, Panel, ...) sind mittels nativer
UI-Ressourcen des zugrunde liegenden OS umgesetzt
Mittels update lässt sich die Darstellung von Komponenten
des AWT im Event Dispatching Thread (inkrementell) steuern
Wie im Beispiel gezeigt, erfordert auch Swing eine Abstimmung mit dem
Event Dispatching Thread
Die Bedienung von Swing ist aber einfacher:
100%-Java lightweight-Komponenten
Größere Auswahl als im AWT
Mehr Features (Double-Buffering, Tooltips etc.)
Anpassungen an heavyweight Komponenten erfordern Eingriffe in die Darstellungshierarchie (mittels super.paintComponent)
Regel: Vermeiden Sie das Mischen von Komponententypen
und bevorzugen Sie lightweight Komponenten
Prof. Dr. Jan Dünnweber, Folie 24 von 27
Verteilte Systeme
Mutual Exclusion in GUIs
Anleitung: Locks & Conditions wie im Beispiel verwenden
Im Beispiel wurde Thread-Safety mittels concurrent.locks
anstelle von klassischen synchronized Monitoren realisiert
Vorteil: concurrent.locks ermöglichen die differenziertere
Vergabe von Sperren
Alle Lock-Klassen implementieren die Schnittstelle:
public interface Lock {
void lock( );
void lockInterruptibly( ) throws ...;
boolean tryLock(long time, TimeUnit unit);
void unlock( ); Condition newCondition( ); }
special features:
Preemtion und Timeouts zur Deadlock-Vermeidung
Locks sind etwas komplizierter zu bedienen, aber flexibler als die
Monitormethoden der Klasse Object
◮ Keine Bindung an die Blockstruktur des Programms
◮ Es existieren nicht-blockierende Varianten des Wartens auf ein
Lock-Objekt (polling):
1 boolean tryLock( );
2 boolean tryLock(long time, TimeUnit u);
Prof. Dr. Jan Dünnweber, Folie 25 von 27
Verteilte Systeme
Zusammenfassung und Ausblick
Swing ist ein high-level Framework für die GUI-Programmierung.
Eine Alternative in Java ist das lower-level Abstract Window
Toolkit (AWT)
Ein zentraler Bestandteil der GUI-Programmierung ist das
Multithreading.
Ereignisse (z.B. Benutzereingaben) werden parallel zum
Programmfortschritt verarbeitet
Ein flexibles Architekturmuster ist Model-View-Controller.
Bei der Implementierung kommt es u. a. auf die korrekte
Thread-Koordination an. Im Beispiel wurde diese mittels
concurrent.locks und Conditions (Deadlock-frei) realisiert
Eine Portierung ins Web erfordert lediglich Anpassungen an View
und Controller
Prof. Dr. Jan Dünnweber, Folie 27 von 27
Verteilte Systeme
Conditions funktionieren in Java wie wait & notify
Die Freigabe von Conditions kann aber an mehrere,
unterschiedliche Bedingungen geknüpft sein:
public interface Condition {
void await( ); void awaitUninterruptibly( ); ...
void signal( ); void signalAll( ); }
Vorsicht : Sind signal oder await nicht von lock und
unlock-Aufrufen umschlossen, so folgt eine
IllegalMonitorStateException!
Prof. Dr. Jan Dünnweber, Folie 26 von 27
Verteilte Systeme
Herunterladen