programmera.net -> java -> normal     för utskrift      info@programmera.net

Villkorssynkronisering

1. Vad är villkorssynkronisering?
2. wait() och notify()
3. Readers/Writers

1. Vad är villkorssynkronisering?

Villkorssynkronisering innebär att en tråd tvingas att vänta tills ett villkor har uppfyllts.

2. wait() och notify()

Då man programmerar villkorssynkronisering måste man explicit skriva att en tråd ska vänta och på vad. I Java finns det inget sätt för en tråd att fördröjja en annan tråd. Det enda sättet att fördröjja en tråd är att tråden säger åt sig själv att vänta. Villkorsfördröjningar programmeras på följande sätt: Varje monitor har en fördröjningslista där trådar kan placeras. Ett objekt kan tvinga den exekverande tråden att till fördröjningslistan med wait(), och sedan låta en annan tråd frigöra den med notify(). Följande regler gäller:

  • wait() placerar tråden i fördröjningslistan för det objektet i vars metod wait() påträffades.
  • wait() kan bara anropas från ett låst objekt, alltså inom ett synchronized-block.
  • notify() frigör en tråd från det egna objektets monitors fördröjningslista.
  • notify() kan bara anropas från ett låst objekt, alltså inom ett synchronized-block.
  • Det är inte säkert att den tråd som stått längst i fördröjningslistan frigörs först.
Nedan beskrivs hur wait() och notify() används:
  1. Tråd1 går in i ett synchronized-block, och låser därmed objektet.
  2. Tråd1 springer på metodanropet wait() som tvingar tråden att låsa upp objektet och ställa sig i monitorns fördröjningslista.
  3. En annan tråd, tråd2, får nyckeln till objektet.
  4. Tråd2 exekverar ett annat synchronized-block i objektet, och låser därmed objektet.
  5. Tråd2 springer på metodanropet notify() som frigör tråd1 ur monitorns fördröjningslista. Tråd1 kan inte köra objektet direkt eftersom tråd2 fortfarande har låset till objektet.
  6. Tråd2 går ur synchronized-blocket och lämnar tillbaka låset till monitorn.
  7. Monitorn ger låset nästa tråd som frågar efter det, kanske tråd1, som då kan fortsätta från den plats den blev fördröjd.

3. Readers/Writers

En brutal lösning på problemet med readers/writers presenterades på sidan  låsning . Den lösningen hade nackdelen att den inte tillåter inte parallella läsningar. Nu kan vi med hjälp av wait() och notify() konstruera en lösning som tillåter parallella läsningar. Vi ersätter classen Controller med:
// CONTROLLER
// This is the class we will modify
// to solve the readers/writers problem
class Controller{
  Resource  res;
  int cr=0; // Number of current readers
  
  public Controller(Resource resource){
    this.res=resource;
  }
  
  public void read(UserThread t){
    synchronized(this){ 
      cr++;
    } 
    res.read(t);
    synchronized(this){
      cr--;
      if(cr==0) notify(); // Signal one waiting reader
    }
  }
  
  public synchronized void write(UserThread t){
  while(cr>0) { 
      try{ wait(); }
      catch(Exception e){} 
    }
    res.write(t);
    notify(); // Signal one waiting reader
  }
}
Följande delar i Controller bör påpekas:

  • Metoden read() är inte längre synkroniserad. De enda delarna som är synkroniserade är tilldelningen av variabeln cr.
  • Eftersom read() (mestadels) inte är synkroniserad kommer write() att anropas trots att det finns läsare som arbetar med read(). Det enda sättet att få reda på om det finns några läsare i read() är att kolla variabeln cr.
  • Om en skrivare anropar write() samtidigt som det finns läsare som kör read() placeras den skrivande tråden i Controllers fördröjningslista. Skrivarna i fördröjningslistan kan bara komma loss genom att någon annan tråd anropar Controller.notify().
  • Sist i read() anropas notify() om det är den sista läsaren som lämnar read(). Detta väcker en sovande skrivare (om det finns någon).
  • Sist i write() anropas notify() för att väcka en sovande skrivare (om det finns någon).
En utskrift av programmet kan se ut på följande sätt:
[olle@dev1]$ java ReadersWriters 3 3 3
All readers and writers are created!
|R     |
|RR    |
|RRR   |
|RR    |
|R     |
|      |
|W     |
|      |
|R     |
|RR    |
|RRR   |
|RR    |
|R     |
|      |
|W     |
|      |
|W     |
|      |
|W     |
|      |
|R     |
|RR    |
|RRR   |
|RR    |
|R     |
|      |
|W     |
|      |
|W     |
|      |
|W     |
|      |
|W     |
|      |
|W     |
|      |
Vi ser att flera läsare R är inne i klassen Resource, men att skrivarna W bara kan gå in en åt gången. Ett allmänt drag för denna metod är att den inte är speciellt rättvis. Läsarna verkar få förtur till resursen. Vi återkommer till problemet på sidan om  semaforer .