|
Zugriff auf gemeinsame Resourcen Beispiel: class Name {
String firstname;
String surname;
public
void setName(String firstname,
String surname) {
this.firstname = firstname;
this.surname = surname;
}
public
String getName() {
return
firstname + " " + surname;
}
}
Problem: Name n;
//-------------
// thread 1:
...
n.setName("Charlie","Chaplin");
...
n.setName("Jerry","Lewis");
...
//-------------
// thread 2:
... n.getName() ...
mögliche Resultate für n.getName(): "Charlie Chaplin"
"Jerry Lewis"
"Jerry Chaplin"
"Charlie Lewis"
Ursache: this.firstname = firstname;
this.surname = surname;
und return
firstname + " " + surname;
sind unterbrechbare (teilbare) Operationen |
|
Synchronisation mit Monitoren: class SyncName {
String firstname;
String surname;
public
synchronized
void setName(String firstname,
String surname) {
this.firstname = firstname;
this.surname = surname;
}
public
synchronized
String getName() {
return
firstname + " " + surname;
}
}
Naiver Implementierungsversuch. class SyncName {
String firstname;
String surname;
boolean busy = false;
public
void setName(String firstname,
String surname) {
while (busy) {}
busy = true;
this.firstname = firstname;
this.surname = surname;
busy = false;
}
public
String getName() {
String res;
while (busy) {}
busy = true;
res = firstname + " " + surname;
busy = false;
return
res;
}
}
setName und getName
sind unteilbare Operationen geworden. Syntax: synchronized modifier ...
synchronized
Type method(...) {
// der gesammte Rumpf ist synchronisiert
// mit this
...
}
oder synchronized Anweisung ...
synchronized ( e ) {
// synchronized statement
// dieser Block wird synchronisiert
// mit dem Objekt, das durch e referenziert wird
...
}
|
|
Problem: Zugang zum Monitor abhängig vom Zustand des Monitors Beispiel: Verbraucher-Prozess möchte Daten aus einem Puffer abholen, der Puffer ist aber noch nicht vom Erzeuger-Prozess gefüllt Lösung:
wait() gibt Monitor für andere Prozesse frei
wait() und notify()
sind in Klassisches Beispiel: Erzeuger-Verbraucher mit Puffer als gemeinsamer Resource class Buffer {
// the two buffer variables
private
boolean empty = true;
private
int value;
//--------------------
// the producer routine
public
synchronized
void put(int v) {
// using if in this context is an error
// example not yet complete !!!
if ( ! empty ) {
// producer must wait until buffer empty
try {
wait();
}
catch ( InterruptedException e ) { }
}
// here the buffer is empty, fill it
value = v;
empty = false;
// notify waiting consumer
notify();
}
//--------------------
// the consumer routine
public
synchronized
int get() {
int v;
// using if in this context is an error
// example not yet complete !!!
if ( empty ) {
// consumer must wait until buffer is full
try {
wait();
}
catch ( InterruptedException e ) { }
}
// here the buffer is full, empty it
v = value;
empty = true;
// notify waiting producer
notify();
return v;
}
}
|
|
Problem:
Zwei threads und zwei Resourcen, Beispiel: class Deadlock {
Object lock1 = new SomeClass();
Object lock2 = new SomeClass();
public void foo() {
synchronized (lock1) {
synchronized (lock2) {
// ...
}
}
}
public void bar() {
synchronized (lock2) {
synchronized (lock1) {
// ...
}
}
}
}
2. Beispiel: Zyklische Struktur mit geschachtelten Monitoraufrufen class X {
Y y1;
public
synchronized
void setY1(Y y) {
y1 = y;
}
public
synchronized
void foo() {
y1.bar();
}
public
synchronized
void bar() {
// ...
}
}
class Y {
X x1;
public
synchronized
void setX1(X x) {
x1 = x;
}
public
synchronized
void foo() {
x1.bar();
}
public
synchronized
void bar() {
// ...
}
}
public
class Deadlock2 {
X x;
Y y;
public Deadlock2() {
x = new X();
y = new Y();
x.setY1(y);
y.setX1(x);
t1 = new MyThread(x,y);
t2 = new MyThread(x,y);
t1.start(); // --> x.foo()
t2.start(); // --> y.foo()
}
}
Die beiden folgenden Programmfragmente erzeugen also, wie in einer früheren Version dieser Seite fälschlicherweise dargestellt, keinen Deadlock, da hier der gleicheThread mehrfach einen Monitor betritt. Dieses ist explizit erlaubt. class X {
synchronized void f() {
// no deadlock: same monitor
f();
}
}
oder class X {
void f() {
synchronized (this) {
synchronized (this) {
// no deadlock: same monitor
}
}
}
}
|
|
Semaphore sind in eine Richtung beschränkte Zähler mit Synchronisation der zählenden Prozesse. Beispiel: //--------------------
public
class CountingSemaphore {
private int count;
public
CountingSemaphore(int initialCount) {
count = initialCount;
}
public
synchronized
void P() {
// if would be an error !!!
while ( count <= 0 ) {
try {
wait();
} catch (InterruptedException e) {}
}
--count;
}
public
synchronized
void V() {
++count;
notify();
}
}
|
|
Problem: Abbrechen eines threads während dieser sich mit der Ausführung in einen Monitor befindet Beispiel: class X {
// ...
synchronized void f() {
// ...
Thread.currentThread().stop();
}
}
|
| Letzte Änderung: 15.06.2009 | © Prof. Dr. Uwe Schmidt |