Wiederholung: Threads

Werbung
Wiederholung: Threads
• Threads können „parallel“ ablaufen
• In Java sorgt die JVM für die Zuteilung von Rechenzeit an
die Threads.
• Beispiele für Anwendung:
• Webserver verarbeitet parallel eingehende Anfragen
• Laufschrift in einer Bildschirmmaske
1
Geschwindigkeitssteigerung durch
Threads?
• Beispiel: Heap-Sort
• Das Herstellen der Heap-Bedingung in verschiedenen
Unterbäumen kann in verschiedenen Threads erfolgen.
• Auf einem Multiprozessorsystem (und nur dort) führt das zu
einer Geschwindigkeitssteigerung
• (Sind allerdings zum Zugriff auf das zu sortierende Feld
Dateizugriffe erforderlich, kann das den Nutzen der parallele
Arbeitsweise wieder zunichte machen.)
• Auf einem Einzelprozessorsystem ist eine
Geschwindigkeitssteigerung durch parallele Threads nur zu
erreichen, wenn beispielsweise:
Thread A intensiv rechnet während
Thread B intensiv die Festplatte nutzt oder
Thread C intensiv vom Netzwerk liest
2
Wiederholung: Threads in Java
class XYZ extends Thread {
public void run() { ... }
}
class Hauptprogramm {
Thread a = new XYZ();
a.start()
}
Alternative zum Erben von Thread: Implementieren des
Interfaces Runnable
3
Mögliche Probleme bei Parallelität
public class Konto {
private int Kontostand;
...
public void zahleEin(int betrag) {
Kontostand = Kontostand - betrag }
public void hebeAb(int betrag) {
Kontostand = Kontostand - betrag }
4
Möglicher Ablauf
Kontostand anfangs = 1234,00
Thread A (500,00 einzahlen)
Thread B (100,00 abheben)
Kontostand lesen: 1234,00
500,00 addieren
Kontostand schreiben: 1734,00
Kontostand lesen: 1734,00
100,00 subtrahieren
Kontostand schreiben: 1634,00
5
Oder auch so...
Kontostand anfangs = 1234,00
Thread A (500,00 einzahlen)
Thread B (100,00 abheben)
Kontostand lesen: 1234,00
Kontostand lesen: 1234,00
500,00 addieren
Kontostand schreiben: 1734,00
100,00 subtrahieren
Kontostand schreiben: 1134,00
6
Zuteilung von Rechenzeit an die
Threads
• Wie im Beispiel gesehen:
• Ohne besondere Vorkehrungen ist nicht vorhersehbar, zu
welchem Zeitpunkt welcher Thread gerade welche
Operation ausführt.
• Folge: Programme mit Threads sind oft schwerer zu testen.
7
Kritische Abschnitte / Monitore
• Ein kritischer Abschnitt ist eine Ressource, auf den
immer nur ein Prozess zugreifen darf.
• Eine typische Art von kritischen Abschnitten sind
Variablen, diese dürfen nicht beliebig parallel gelesen
und geschrieben werden.
• Sperren sind ein Mittel, um den parallelen Zugriff auf
Ressourcen zu reglementieren.
• Es ist möglich, kritische Abschnitte zu schützen, so dass
nur ein Thread zu einem Zeitpunkt darauf zugreifen darf.
Dies geschieht mithilfe so genannter Monitore (Locks).
8
Synchronisierter Ablauf
Kontostand anfangs = 1234,00
Thread A (500,00 einzahlen)
Belegt den Monitor auf das zu
ändernde Konto-Objekt
Kontostand lesen: 1234,00
500,00 addieren
Kontostand schreiben: 1734,00
Thread B (100,00 abheben)
Darf nicht zugreifen, da sich
Thread A das Objekt per
Monitor „reserviert“ hat
Thread B reiht sich in
Warteliste ein
Gibt den Monitor auf das
geänderte Konto-Objekt
wieder frei
Kontostand lesen: 1734,00
100,00 subtrahieren
Kontostand schreiben: 1634,00
9
Synchronisation
• In Java: Schlüsselwort synchronized
• (Ab Java 5: Alternative java.util.concurrent.locks.Lock,
hier nicht behandelt)
• Für ein Objekt als synchronized definierte Methoden
(oder Blöcke) darf immer nur ein Thread zugleich
ausführen.
• Die anderen Threads kommen in eine Warteschlange, in
der alle Threads verwaltet werden, die die synchronized
Methode ausführen wollen.
• Threads kommen in diese Warteschlange, wenn:
• sie eine synchronized Methode aufrufen, während ein
anderer Thread auf das Objekt zugreift,
• wenn sie in der aufgerufenen, synchronized Methode
yield() aufrufen.
10
Beispiel für Monitor
public class Konto {
private int Kontostand;
...
public synchronized void zahleEin(int
betrag) {
Kontostand = Kontostand - betrag }
public synchronized void hebeAb(int betrag)
{
Kontostand = Kontostand - betrag }
11
Monitorkonzept
• Es lassen sich sowohl einzelne Blöcke, als auch ganze
Methoden schützen:
• Eine ganze Methode kann durch Kennzeichnung von
synchronized geschützt werden, z. B.:
public synchronized void zahleEin{ ...
}
• Ein Block kann durch
synchronized(ObjektName) {
...
}
geschützt werden. Hierbei wird der Monitor des Objektes
ObjektName belegt.
12
Parallelitätsregeln
• Wenn zwei oder mehr Threads auf ein Objekt zugreifen...
• ... gibt‘s kein Problem, wenn alle nur lesen.
• Wenn jedoch ein Thread den Inhalt des Objektes
veränert, so sollten alle Methoden, die auf das Objekt
zugreifen, als synchronized deklariert werden.
• Wenn eine Methode auf das Ergebnis eines anderen
Threads warten muss, um das Objekt zu ändern, dann
sollte wait() aufgerufen werden.
• Wenn eine synchronized Methode ihr Objekt geändert
hat, dann sollten die gerade wartenden Prozesse mit
notifyAll() alarmiert werden.
13
Methoden der Klasse Object
• Monitor vorübergehend freigeben:
• Mit der Methode
public final void wait() throws
InterruptedException
wird der Monitor freigegeben und der Thread geht in einen
Wartezustand, bis er von einem anderen Thread durch notify()
oder notifyAll() geweckt wird
• Nach Wecken setzt der Thread seine Arbeit an gleicher Stelle fort
• Wichtig, um auf nicht freie Resourcen zu warten
=> andere Threads müssen die Chance bekommen, die Ressourcen
freizugeben
14
Methoden der Klasse Object
•
Wird für einen Thread wait() aufgerufen, kommt er in die Wartemenge
des Objektes (nicht zu verwechseln mit der bereits erwähnten
Warteschlange der Threads, die einen Monitor erlangen wollen!)
•
•
•
Wir haben also zwei Sorten von „warten“
WAITING: warte auf notify, Thread nimmt nicht an der Vergabe der
Rechenzeit teil.
BLOCKED: warte darauf, einen Monitor zu bekommen
•
Aufwecken von Threads im Status WAITING
• Die Methode public final void notify() der Klasse Object
benachrichtigt einen Thread aus der Warteliste auf den zum Objekt
zugehörigen Monitor (willkürliche Auswahl)
• Die Methode public final void notifyAll() der Klasse Object
benachrichtigt alle Threads aus der Warteliste, diese werden um den
Monitor konkurrieren
15
Datenaustausch zwischen Threads
•
Wenn Daten zwischen Threads ausgetauscht werden sollen, gibt es
Synchronisationsprobleme.
•
•
•
Unser Beispiel: zwei parallele Threads:
Thread Erzeuger erzeugt Daten
Thread Verbraucher verarbeitet (verbraucht) diese
•
Kommunikation zwischen den beiden Threads erfolgt über eine Klasse
Tunnel: Sie soll von einem Thread jeweils ein Paket Daten aufnehmen
und an den anderen Thread weitergeben.
Klar ist: Verbraucher-Thread muss warten, bis Daten im Tunnel
vorhanden sind. Der Erzeuger-Thread soll erst dann wieder etwas
erzeugen, wenn der Verbraucher-Thread aus dem Tunnel gelesen hat
(d.h. Tunnel kann genau einen Wert aufnehmen).
•
16
Handshake-Verfahren: Erzeuger
class Erzeuger extends Thread {
private Tunnel t;
public Erzeuger(Tunnel tunnel) {
t = tunnel;
}
public void run() {
for (int i=0; i<5; i++) {
t.geben(i);
System.out.println("Erzeuger schreibt " + i);
try {
Thread.sleep((int)(Math.random() * 100));
} catch (InterruptedException e) {
}
}
}
}
17
Handshake-Verfahren: Verbraucher
class Verbraucher extends Thread {
private Tunnel t;
public Verbraucher(Tunnel tunnel) {
t = tunnel;
}
public void run() {
int wert = 0;
for (int i=0; i<5; i++) {
wert = t.nehmen();
System.out.println("Verbraucher liest " + wert);
}
}
}
18
Handshake-Verfahren: Tunnel
class Tunnel {
private int inhalt;
private boolean DatenVorhanden = false;
public synchronized int nehmen() {
// Liest int aus Tunnel
while (DatenVorhanden == false) {
try {
wait();
} catch (InterruptedException e){}
}
DatenVorhanden = false;
notifyAll();
return inhalt;
}
public synchronized void geben(int wert) {
// Schreibt int in Tunnel
while (DatenVorhanden == true) {
try {
wait();
} catch (InterruptedException e) {}
}
inhalt = wert;
DatenVorhanden = true;
notifyAll();
}
}
19
Handshake-Verfahren: StartKlasse
class StartKlasse {
public static void main(String[] args) {
Tunnel t = new Tunnel(); // Das Objekt mit dem Monitor
Erzeuger e = new Erzeuger(t);
Verbraucher v = new Verbraucher(t);
e.start();
v.start();
}
}
Ausgabe:
Erzeuger schreibt
Verbraucher liest
Verbraucher liest
Erzeuger schreibt
Verbraucher liest
Erzeuger schreibt
Verbraucher liest
Erzeuger schreibt
Verbraucher liest
Erzeuger schreibt
0
0
1
1
2
2
3
3
4
4
Hinweis: nehmen und geben ist synchronisiert, nicht aber die Ausgabe!
20
Deadlocks
• Vorsicht! Mit dem beschriebenen Vorgehen kann man
mühelos einen sog. Deadlock (Verklemmung) erzeugen:
• Es gibt zwei kritische Ressourcen x und y, jede hat einen
Monitor. Zwei Threads alpha und beta versuchen beide auf
diese Ressourcen zuzugreifen. Jeder dieser Threads kann
seine Aufgabe erst dann erfüllen, wenn er die Monitore auf
beide Ressourcen erhalten hat.
•
•
•
•
Mögliche Situation:
alpha bekommt Monitor auf x
beta bekommt Monitor auf y
Beide warten nun vergeblich auf den jeweils anderen
Monitor.
21
VE 12
Java Bibliotheken
io und util
22
Überblick (Auszug)
•
•
•
•
•
•
•
•
•
•
•
java.applet
java.awt
java.beans
java.io
java.lang
java.net
java.rmi
java.security
java.sql
java.util
javax.swing
Applet-Programmierung
GUI-Programmierung mit AWT
JavaBeans™-Entwicklung
Ein-/Ausgabe-Operationen, Datenströme
fundamentale Klassen (z.B. String)
Netzwerkfunktionen
Remote Method Invocation
Zertifikate, Kryptographie
Datenbank-Funktionen
Klassen für Datenstrukturen
GUI-Programmierung mit Swing
• und viele mehr (XML, CORBA, Namensdienste, Sound...)
23
Java API Specification
24
Dateien und Streams
java.io.*
25
Motivation
Wofür braucht man Ein- und Ausgaben?
• Datenaustausch zwischen
Mensch und Maschine (trivial ☺)
• für die Kommunikation von Maschinen
untereinander (z.B. Rechner ↔ Drucker)
• um Persistenz von Objekten (also Daten) zu ermöglichen
26
Persistenz
• Von Persistenz eines Objektes spricht man:
• wenn das Objekt das Ende einer Programmausführung überlebt und
• von einem anderen Programm (oder einer anderen Ausführung des
gleichen Programms) benutzt werden kann.
• Persistent werden Objekte, indem man sie speichert.
Speicherort kann das Dateisystem sein oder auch eine
Datenbank.
27
Persistenz
• Die Beschreibung der Struktur persistenter Objekte wird das
Format genannt.
• Problem: wie werden Objekte persistent gemacht?
• Wie lassen sich solche persistent gemachten Objekte
wieder einlesen?
• Wie einigen sich Schreiber und Leser auf Formate?
• Java-Ansatz: Eine Reihe von Formaten wird in Form von
Klassen und Interfaces vordefiniert. Hierzu gehört das
eigentliche Format und natürlich auch der schreibende bzw.
lesende Zugriff.
28
Ganz unten: Dateisystem
•
•
Computer speichern Daten auf verschiedenen Medien (Festplatten, CD,
DVD etc.) in Dateisystemen
diese bestehen aus
• Dateien (enthalten die eigentlichen Daten) und
• Verzeichnisstruktur (organisiert Dateien und hält Info bereit)
•
Dateien haben Attribute
• Name, Typ, Ort, Größe etc
•
und es gibt spezifische Methoden
•
•
•
•
neue Dateien erzeugen
in Dateien schreiben
aus Dateien lesen
Dateien löschen...
29
Ganz unten: Dateisystem
• Verzeichnisstruktur organisiert Dateien in einem
Verzeichnisbaum
• und es gibt typische Operationen:
•
•
•
•
Inhalt eines Verzeichnisses anzeigen
Unterverzeichnis anlegen
Unterverzeichnis löschen
Dateien in Verzeichnis auflisten...
30
Zugriff auf Dateisystem in Java
•
•
•
Natürlich ist eine Datei in Java wieder ein Objekt, und zwar von der
Klasse java.io.File
Beachte: Dieses Objekt steht für den Speicherort einer Datei, nicht für
den Inhalt der Datei.
Objekte werden erzeugt, um Dateien erzeugen, öffnen und schließen zu
können und für Zugriffe auf das Dateisystem
import java.io.File
public void erzeugeDatei {
File datei = new File("Test.txt");
System.out.println("Dateiname: " +
datei.getName());
}
Ausgabe:
Dateiname: Test.txt
31
java.io.File
• Konstruktor:
• public File(String pathname)
throws NullPointerException
• Erzeugt Objekt über Pfadnamen
• Prüfung von Eigenschaften:
•
•
•
•
•
•
•
public
public
public
public
public
public
public
boolean exists()
boolean canRead()
boolean canWrite()
boolean isFile()
boolean isDirectory()
boolean isHidden()
long length()
32
java.io.File
• Methoden:
• public boolean delete()
• Löscht Datei oder Verzeichnis
• public void deleteOnExit()
• Löscht bei Beendigung der Virtual Machine
• Flag wird gesetzt, welches nicht zurückgenommen werden kann
• public boolean mkdir()
• Erstellt Verzeichnis
• public boolean mkdirs()
• Erstellt Verzeichnis und eventuell benötigte, nicht existierende
Mutterverzeichnisse
33
java.io.File
• public String[] list()
• liefert Array mit Auflistung aller Einträge in Verzeichnis, das
durch Namen des File-Objekts gegeben ist
• public String[] list(FilenameFilter filter)
• liefert Array mit Auflistung aller Einträge in Verzeichnis, das
durch Namen des File-Objekts gegeben ist
• diese Auflistung ist jedoch gefiltert, dies wird von filter
übernommen
• filter ist Objekt einer Klasse, die das Interface
FilenameFilter implementiert
34
Gefilterte Dateiliste
• Interface FilenameFilter kann von einer Klasse
implementiert werden
• dadurch lässt sich das Ergebnis von File.list() beeinflussen
• Inhalt des Interfaces FilenameFilter:
public interface FilenameFilter {
boolean accept(File dir, String name);
}
• Es reicht, die accept()-Methode sinnvoll zu
implementieren
35
Gefilterte Dateiliste - Beispiel
• Beispiel: Filter, der nur Dateinamen durchlässt, die eine
bestimmte Zeichenkette enthalten
public class DirFilter implements FilenameFilter {
String afn;
DirFilter(String afn) { this.afn = afn; }
public boolean accept(File dir, String name) {
String f = new File(name).getName();
return f.indexOf(afn) != -1;
}
}
36
Gefilterte Dateiliste - Beispiel
• Verwendung des Filters:
File path = new File(".");
String[] list;
list = path.list(new DirFilter("xyz"));
• sorgt dafür, dass in list[] nur die Dateinamen aus dem
aktuellen Verzeichnis („.“) aufgenommen werden, die „xyz“
enthalten.
37
Herunterladen