Seminararbeit - Präsentation

Werbung
Vorlesung Betriebssysteme
Threads in Java
Vortrag im Rahmen der
Vorlesung Betriebssysteme
an der FH München
von Thomas Wöllert
([email protected])
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Inhalt
Erzeugen von Threads
Erweitern der Klasse „Thread“
Das Interface „Runnable“
Synchronisation
Out-of-Sync Beispiel
Schlüsselwort „synchronized“
Variabeln „volatile“
„wait()“ und „notify()“
Probleme
Weiter bestehende Problemfälle
Bekannte Probleme der Klasse „Thread“
„java.util.Timer“
Erzeugen eines „Timer“ und „TimerTask“
Beispielprogramm: Nutzung von „Timer“(n)
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Erzeugen von Threads
Erweitern der Klasse „Thread“
// Datum Thread
class DateThread extends Thread
{
public void run()
{
for ( int i = 0; i < 20; i++ )
System.out.println( new Date() );
}
}
// Zähler Thread
class CounterThread extends Thread
{
public void run()
{
for ( int i = 0; i < 20; i++ )
System.err.println( i );
}
}
// Start ThreadDemo
public class ThreadDemo
{
public static void main( String args[] )
{
Thread t1 = new DateThread();
t1.start();
Ausgabe:
Mon Oct 11 19:06:49 CEST 2004
0
1
2
3
4
5
6
7
8
9
Mon Oct 11 19:06:49 CEST 2004
10
[...]
Thread t2 = new CounterThread();
t2.start();
}
}
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Erzeugen von Threads
Das Interface „Runnable“
// Datum Runnable
class DateRunnable implements Runnable
{
public void run()
{
for ( int i = 0; i < 20; i++ )
System.out.println( new Date() );
}
}
// Zähler Runnable
class CounterRunnable implements Runnable
{
public void run()
{
for ( int i = 0; i < 20; i++ )
System.err.println( i );
}
}
// Start RunnableDemo
public class RunnableDemo
{
public static void main( String args[] )
{
Thread t1 = new Thread( new DateRunnable() );
t1.start();
Ausgabe:
Mon Oct 11 19:06:49 CEST 2004
0
1
2
3
4
5
6
7
8
9
Mon Oct 11 19:06:49 CEST 2004
10
[...]
Thread t2 = new Thread( new CounterRunnable() );
t2.start();
}
}
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Synchronization
Out-of-Sync Beispiel
public class IncrementDemo {
public class IncThread implements Runnable {
// Die inkrement Variabeln
private int a = 0;
private int b = 0;
// Der Name des Thread und das IncrementDemo Objekt
private String name = "";
private IncrementDemo incDemo = null;
public IncrementDemo() {
Thread t1 = new Thread(new IncThread("A", this));
Thread t2 = new Thread(new IncThread("B", this));
public IncThread(String name, IncrementDemo incDemo) {
name = name;
incDemo = incDemo;
}
}
t1.start();
t2.start();
public void run() {
// Inkrementiere 'a' und 'b' 10.000.000 mal
for ( int i = 0; i < 10000000; i++ ) { incDemo.inc(); }
public void inc() {
a++;
b++;
}
// Hole den Wert von 'a' und 'b'
int a = incDemo.getA();
int b = incDemo.getB();
public int getA() { return a; }
public int getB() { return b; }
// Prüfe 'a' und 'b' auf Gleichheit
if(a != b) {
System.err.println("ERR: " + name + "/" + a + "/" + b);
} else {
System.out.println("SUCCESS: " + name);
}
// Startup Main
public static void main( String args[] ) {
new IncrementDemo();
}
}
}
}
Ausgabe:
ERR: B/11492019/16066571
ERR: A/15425448/20000000
Betriebssysteme – WS 2004/05
SUCCESS: A
SUCCESS: B
SUCCESS: B
SUCCESS: A
Threads in Java
ERROR: B/16832558/12230014
ERROR: A/20000000/15397456
T.Wöllert ([email protected])
Synchronization
Schlüsselwort „synchronized“
Synchronization auf Methoden
public void inc() {
public synchronized void inc() {
a++;
b++;
a++;
b++;
}
}
Synchronization auf Blöcke
// Inkrementiere 10.000.000 mal
for ( int i = 0; i < 10000000; i++ ) {
// Inkrementiere 10.000.000 mal
for ( int i = 0; i < 10000000; i++ ) {
incDemo.inc();
synchronized( incDemo )
incDemo.inc();
}
}
{
}
Ausgabe:
SUCCESS: A
SUCCESS: B
SUCCESS: A
SUCCESS: B
SUCCESS: B
SUCCESS: A
SUCCESS: A
SUCCESS: B
aber: erhöhte Laufzeit, da die Threads aufeinandere warten müssen
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Synchronization
Variabeln „volatile“
Es ist nicht gesichert, dass die einzelnen Bytecode-Befehle zum Ändern von langen
Datentypen (z.B. long und double), unteilbar sind. Ein Thread kann also mitten in der
Änderung einer dieser Variabeln von einem anderen Thread verdrängt werden.
Beispiel: LONG Variable
long exampleLong = 0x1234567890abcdefL;
Zwei Threads wollen nun diese Variabel ändern :
Thread 1 auf 0x00000000 00000000L;
Thread 2 auf 0xabcdef12 34567890L;
Wenn hier Thread 1 beginnt den ersten Teil der long-Variabeln mit 00000000 zu
beschreiben und dann Thread 2 seine Arbeit beginnt ist die Variabel fälschlicherweise
mit 00000000 90abcdefL initialisiert.
Thread 2 beginnt jetzt auch den ersten Teil der Variabeln zu überschreiben was zu
folgendem krummen Wert führt : abcdef12 90abcdefL
Lösung: „volatile“
volatile long exampleLong = 0x1234567890abcdefL;
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Synchronization
„wait()“ und „notify()“
Szenario: Thread 1 liefert Daten, die ein Thread 2 verwenden möchte. Thread 2 möchte
allerdings nicht in einer kostspieligen Schleife auf diese Daten warten.
Beide Threads synchronisieren sich über ein bekanntes Objekt. Hierdurch kann Thread 2
schlafen und der Prozessor andere Aufgaben übernehmen bis Thread 1 mitteilt, dass die
Daten vorhanden sind.
Thread T1 Code:
Thread T2 Code:
synchronized ( o ) {
// Habe die Daten erzeugt
synchronized ( o ) {
o.wait();
// Benachrichtige jetzt meinen Wartenden
o.notify();
}
// Verarbeite Daten
[...]
}
Da sich beide Threads über das Objekt „o“ synchronisieren wird Thread T2 nach
„o.notify()“ aufgeweckt und kann die Verarbeitung der nun zur Verfügung stehenden
Daten fortsetzen.
Achtung: Es ist nicht garantiert, dass der erste mögliche Thread der „o.wait()“
aufgerufen hat, auch der erste ist, der durch „o.notify()“ wieder aufgeweckt wird.
Hierfür gibt es „o.notifyAll()“, welches alle am Objekt „o“ wartenden Threads aufweckt !
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Probleme
Weiter bestehende Problemfälle
Szenario: Thread 1 will über eine, mit Daten gefüllte Liste, iterieren. Zur selben Zeit
verändert Thread 2 die Anzahl der Elemente in der Liste.
Thread T1 Code:
public void iteriere() {
public void iteriere() {
// Hole Iterator der Liste
Iterator it = list.iterator();
// Laufe durch die Liste
for( int i = 0; i < list.size(); i++ ) {
// Iteriere über die gesamte Liste
while(it.hastNext()) {
// Bearbeite aktuelles Listenelement
[...]
}
// Bearbeite aktuelles Listenelement
[...]
}
}
}
Gefahr in beiden Fällen:
●
T1 bearbeitet ein Element mit falschen Daten (Änderung durch T2 nicht abgeschlossen)
●
T1 versucht ein „NULL“-Element zu bearbeiten (Element wurde gerade von T2 gelöscht)
Lösung:
●
Sperren einzelner Datenelemente während Änderungen (nicht der ganzen Liste)
●
Prüfung ob das erhaltene Listenelement „NULL“ ist.
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Probleme
Bekannte Probleme der Klasse „Thread“
Auszug aus der Java v5.0 API – Class „Thread“:
void destroy()
Deprecated. This method was originally designed to destroy this thread without any
cleanup. Any monitors it held would have remained locked. However, the method was
never implemented. If if were to be implemented, it would be deadlock-prone in much
the manner of suspend(). If the target thread held a lock protecting a critical system
resource when it was destroyed, no thread could ever access this resource again. If
another thread ever attempted to lock this resource, deadlock would result. Such
deadlocks typically manifest themselves as "frozen" processes. For more information,
see „Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?“.
Selbiges gilt für andere Methoden dieser Klasse :
●
resume()
●
stop()
●
suspend()
Dies macht es sehr schwierig einen Thread während der Laufzeit zu stoppen, wieder zu
starten oder zu zerstören.
Lösung:
●
Nutzung der „Timer“ und „TimerTask“ Klassen
●
Manuelle Steuerung des „Timer“ Threads
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
„java.util.Timer“
Erzeugen eines „Timer“ und „TimerTask“
// Der zu erfüllende Task
class Task extends TimerTask
{
public void run()
{
System.out.println( "Doing something." );
}
}
// Start TimerDemo
public class TimerDemo
{
public static void main( String args[] )
{
Timer timer = new Timer();
// Nach 2 Sekunden geht es los
timer.schedule( new Task(), 2000 );
// Nach 1 Sekunde geht es los und
// dann alle 5 Sekunden
timer.schedule( new Task(), 1000, 5000 );
}
}
●
●
●
●
Erlaubt eine genauere zeitliche Steuerung von Abläufen (Startzeit, Periodenzeit)
Es wird keine endlos „while(true)“-Schleife mehr für zyklische Threads benötigt
Keine „deprecated“-Methoden in der API
Einfachere manuelle Steuerung -> keine „IllegalThreadStateException()“
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
„java.util.Timer“
Beispielprogramm: Nutzung von „Timer“(n)
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Ende – End – Fin - Conclude
Vielen Dank für Eure
Aufmerksamkeit !
Fragen ?
Betriebssysteme – WS 2004/05
Threads in Java
T.Wöllert ([email protected])
Herunterladen