Chapter 14
Samtidighed i Java

Kurt Nørmark ©
Department of Computer Science, Aalborg University, Denmark


September 2001


Abstract
Previous lecture Next lecture
Index References Contents
Programmer med samtidighed er et stort og vigtigt emne. I denne lektion introducerer og motiverer vi først emnet i forhold til den slags programmering, vi hidtil har studeret. Dernæst ser vi på et antal vigtige og klassiske problemstillinger i forbindelse med samtidighed. En væsentlig årsag til at dyrke emnet på dette kursus skal tilskrives Java's understøttelse af samtidighed. I Java taler man om 'tråde'. Vi gennemgår i denne lektion Java's forskellige muligheder for programmering af flere samtidige tråde inden for rammerne af ét program. Vi ser derimod ikke på samtidighed, som udspiller sig mellem to eller flere Java programmer, som kører på forskellige maskiner.


Introduktion og motivation

Vores programmering indtil nu...
Slide Note Contents Index
References 
Lad os starte med at karakterisere den slags programmering, vi har bedrevet indtil vi nu i denne lektion bliver introduceret for mulighederne for samtidighed

Når vi programmerer er vi vant til at skulle fastlægge rækkefølgen af alle de handlinger, vi foreskriver. Ofte er rækkefølgen væsentlig. Men til andre tider er rækkefølgen ligegyldig. Når sidstnævnte er tilfældet åbner vi i mange tilfælde op for betydelig bedre programmer ved ikke at insistere på en bestemt rækkefølge af de foreskrevne handlinger. Denne observation er en passende måde at starte dagens emne, som vi har valgt at kalde samtidighed

Indtil denne lektion har vore programmer beskrevet handlinger i én ganske bestemt rækkefølge

The concept sekventielt programforløb: Et sekventielt programforløb udfører beregningsenheder i en bestemt rækkefølge. Én beregningsenhed starter først når den forrige er afsluttetI et sekventielt programforløb, eller blot i et sekventielt program, udføres de enkelt beregningsenheder i en rækkefølge, som er bestemt af programmets kontrolstrukturer. Beregningsenhederne er primært kommandoer, men også de enkelte dele af udtryk bør i denne sammenhæng betegnes som beregningsenheder

  • Vi siger at et sekventielt program udviser deterministisk adfærd:

    • Determinisme indebærer at enhver handling er en uundgåelig konsekvens af de foregående handlinger

    • Det er fuldstændigt forudbestemt i hvilken rækkefølge kommandoerne i et sekventielt program bliver udført

      • Brugerinput kan dog siges at udgøre et nondeterministisk element

    • Hvis et program kører flere gange med samme input giver det samme resultat hver gang

Vi vil i denne lektion studere ikke-sekventielle programmer som kan udvise nondeterministisk adfærd

Naturlig samtidighed
Slide Note Contents Index
References 
Det er velkendt fra utallige hverdagssituationer at ting foregår samtidigt. Dette 'fact of life' har vi naturligvis lyst til at kunne overføre til de handlinger, som foreskrives i de programmer, vi laver. Dette fænomen vil vi her diskutere under overskriften 'naturlig samtidighed'.

I mange programmer findes der naturligt et antal forløb, som kan udføres samtidigt

  • Samtidige forløb:

    • Velkendt fra den virkelige verden

    • Derfor naturlig i vores modellering af den virkelige verden med henblik på programmering af EDB-systemer

    • Derfor attraktivt at understøtte i programmeringssprog

Som et eksempel på samtidighed fra vores hverdag kan vi tænke på handlinger, som udføres af to eller flere personer. Hvis vi ønsker at simulere dette på en computer i et sekventielt programforløb kan dette blive ganske svært. Vi vil måske i en første simpel løsning skiftevis simulere en handlig fra den ene person efterfulgt af en handling fra den anden person. I en mere realistisk simulering bliver vi nødt til at tage hensyn til, at den ene person kan udføre mange handlinger i et tidrum, hvor den anden person er inaktiv. Det vil være særdeles ønskværdigt at kunne modellere de to personers samtidige handlinger mere naturligt i et program.

Uden programmeringsunderstøttelse af samtidighed tvinges vi til at sekventiere samtidige handlinger på en bestemt måde

En påtvungen sekventiering af naturlig samtidighed giver ofte komplekse og uigennemskuelige programmer

Afgrænsning
Slide Note Contents Index
References 
Da samtidighedsproblematikken er stor og bred vil vi her afgrænse os til at se på et bestemt, og relativt snævert aspekt

Samtidighed er et stort emne, som ikke vil blive behandlet i dybden på dette kursus

  • Udvalgte områder inden for samtidighed

    • Flere 'tråde' i ét program

      • Emnet i denne lektion

      • Specielt 'naturlig samtidighed'

    • Flere samarbejdende programmer på forskellige maskiner

      • Distribuerede programmer

      • Kommunikation over netværk

    • Parallelle algoritmer

      • Algoritmer hvor samtidighed udnyttes for at skaffe større effektivitet

      • Kræver flere processorer (CPU'er)

    • Samtidighed i operativsystemer

      • Realisering af en illusion om samtidighed på sekventiel hardware

I kurserne 'Principper for samtidighed og styresystemer' og 'Modeller og værktøjer til parallelitet' på Dat2 vil der være mulighed for at lære meget mere om emnet

Terminologi
Slide Note Contents Index
References 
Vi vil ikke her gå i pedantiske detaljer om terminologi, men blot bemærke at også inden for dette område bruges der mange forskellige ord

Der benyttes mange forskellige ord når man interesserer sig for samtidighedsemner

Table. En række ord på dansk og engelsk som benyttes når vi interesserer os for samtidighed
DanskEngelsk
SamtidighedConcurrency
ParallelitetParallelism
ProcesProcess
TrådThread
-Task
 


Begreber og problemstillinger

Grundliggende antagelser
Slide Note Contents Index
References 
Vi starter denne afdeling med en meget væsentlig antagelse, som på en grundliggende måde vil påvirke vores programmering med samtidige forløb

Den indbyrdes hastighed af samtidige programforløb er ukendt

  • Et program med samtidighed kan udvise nondeterministisk adfærd:

    • Rækkefølgen af handlinger i forskellige programforløb kendes kun sjældent

    • To kørsler af samme program (med samme input) kan give forskellige resultater

Antagelser om programforløb's indbyrdes hastigheder kan ikke benyttes til at ræsonnere om et programs korrekthed

Oversigt over væsentlige problemstillinger
Slide Note Contents Index
References 
Vi vil også her på dette tidlige sted i lektionen benytte lejligheden til at give en samlet oversigt over nogle af de væsentlige problemstillinger vi møder, når interessen samler sig om samtidighed

  • Hvorledes afvikles, kontrolleres og skeduleres samtidige programforløb på en computer med kun én CPU?

    • Programmeringssproglige virkemidler

    • Retfærdig adgang til CPU'en

  • Hvorledes indpasses samtidighed i objekt-orienteret programmering?

  • Hvorledes sikres udelelig adgang til fælles ressourcer?

  • Hvorledes koordineres samtidige programforløb med henblik på indbyrdes synkron interaktion?

  • Hvordan undgår vi at et program med samtidige programforløb går i baglås?

  • Under hvilke omstændighed skal vi overveje flere samtidige programforløb når vi kun har én CPU?

Vi studerer kun en delmængde af ovenstående problemstillinger i denne lektion

Samtidighed i forhold til OOP
Slide Note Contents Index
References 
Nøgleordet, som vi nu introducerer er aktive objekter. Aktive objekter kommer til at danne kontrast til de objekter vi indtil nu har studeret, og som vi naturligt her vil kalde passive objekter

Samtidighed behandles naturligt i objekt-orienteret programmering ved at tale om aktive objekter

The concept aktivt objekt: Et aktivt objekt er et objekt, der ud over variable og metoder har sig eget selvstændige og sekventielle programforløbEt aktivt objekt udviser selvstændig adfærd på en sådan måde, at objektet kan ændre tilstand uden at det bliver påvirket gennem dets klientgrænseflade. En aktivt objekt er udgangspunktet for et selvstændigt programforløb. I billedsprog kan vi sige, at dette programforløb giver objektet et selvstændigt liv
The concept passivt objekt: Et passivt objekt er et objekt med variable og metoder, som kun kan ændre tilstand som følge af påvirkninger fra andre objekterSom en kontrast til aktive objekter vil vi tale om passive objekter. Et passivt objekt er altså et ikke-aktivt objekt. Et passivt objekt kan ikke ændre tilstand 'af sig selv', men kun gennem påvirkninger fra andre objekter (som måske er påvirket af tredje objekter, som måske ultimativt er aktive objekter)

I de hidtidige programmer vi har skrevet har alle 'vore egne objekter' været passive

Det 'aktive element' har ophav i den statiske metode main, som ikke er tilknyttet noget bestemt objekt


Tråde i Java

Trådbegrebet i Java
Slide Note Contents Index
References 
Inden vi rigtig går igang med at rulle Java's understøttelse af tråde ud vil vi karakterisere Java's tråd begreb

The concept tråd: En tråd er et sekventielt programforløb i et Java program, som tager udgangspunkt i et objekt af typen ThreadEn tråd er betegnelsen for et sekventielt programforløb inden for rammerne af et Java program

  • Karakteristik af en tråd i Java

    • Et Java program består af én eller flere tråde

    • En tråd kan ækvivaleres med et aktivt objekt

    • En tråd er en 'letvægtsproces' inden for rammerne af et program

      • Kontrast: Et program udføres i en proces af et operativsystem

    • Hver tråd har sit eget 'bogholderi' med bl.a. programtæller og stakken af aktiverede metoder

Interfacet Runnable
Slide Note Contents Index
References 
Metoden run, specificeret i interfacet Runnable, er udgangspunktet for tråde i Java

Interfacet Runnable udgør grundlaget for enhver tråd i Java

  • Karakteristik af Runnable

    • Et meget simpelt interface

    • Annoncerer kun én metode, som hedder run

    • Metoden run i en klasse som implementerer Runnable er udgangspunktet for udførelsen af en tråd i Java

    • Spiller sammen med klassen Thread når en tråd skal startes og udføres

Reference

Trådskabelse gennem subklasse af Thread
Slide Note Contents Index
References 
Vi ser først på hvordan vi laver en ny tråd i et Java program ved at at lave en subklasse af klassen Thread, som redefinerer metoden run

En tråd kan skabes ved at instantiere en klasse som implementerer Runnable

Klassen Thread implementerer Runnable med en tom run metode

Programmering af en tråd kan ske ved at lave en subklasse af Thread som redefinerer run

Program: En subklasse af Thread samt start af en tråd med metoden start.
class FirstThread extends Thread { 
 
  Tråd tilstand;
   
  FirstThread(String trådnavn, andre parametre){
    super(trådnavn);
    Tråd initialisering;
  }

  public void run() {
    Tråd program start;
  }
}

...
FirstThread t = new FirstThread("Første tråd", andre aktuelle parametre); 
t.start();
...

Metoden start i klassen Thread aktiverer metoden run

Efter start kører der (mindst) to samtidige tråde: tråden hvori start forekommer og den nystartede tråd

Reference

Trådskabelse ved implementering af Runnable
Slide Note Contents Index
References 
Som et alternativ kan man lave sin egen klasse som implementerer interfacet Runnable. En instans af denne klasse skal dog overgives til en instans af Thread for at kunne virke som en tråd i et Java program

Ofte kan man ikke tillade sig at lave en subklasse af Thread, idet en 'tråd-klasse' har behov for at arve fra en anden klasse

Der er derfor behov for en alternativ teknik til skabelse af en tråd i Java, hvor Thread objektet delegerer run arbejdet til et andet objekt

Program: Installering af af en 'Runnable objekt' i et Thread objekt'. Læg mærke til at instansen af SecondThread overføres som den første parameter til Thread konstruktoren. Den pågældende Thread constructor vil aktivtere run i Runnable objektet i stedet for at kalde run i thread objektet. Slå selv op i Threads konstruktor dokumentation, og bliv overbevist.
class SecondThread implements Runnable{

  Tråd tilstand;
  
  SecondThread(parametre){
    Tråd initialisering;
  }

  public void run() {
    Tråd program start;
  }
}

...
SecondThread st = new SecondThread(aktuelle parametre);
Thread realTread = new Thread(st, "Anden tråd");
realThread.start();
...

References

Egenskaber af klassen Thread
Slide Note Contents Index
References 
Vi vil her se nærmere på egenskaberne af klassen Thread

  • Klassen Thread har ansvar for

    • Tråd konstruktion via et antal forskellige konstruktorer som understøtter initialisering med forskellige kombinationer af navn, runnable objekt, og thread gruppe

    • Trådnavngivning

    • Trådskedulering: start, stop, yield, interrupt, resume, mv. af en tråd

    • Statiske metoder til aflæsning af 'current thread', antal aktive threads, mv.

    • Konstanter som angiver minimum, maximum og normal prioriteter af tråde

Thread navnet bruges til at identificere en tråd, f.eks. i udskrifter o.l.

Reference

Eksempel på et program med tråde: Skjald
Slide Note Contents Index
References 
Lad os nu se på vores første egentlige program med flere tråde. Eksemplet viser en 'skjald' som synger en sang. Samtidigheden kommer ind i billedet når to skjalde synger hver sin sang

Program: Klassen Skjald som arver fra Thread. Skjald starter en tråd, som synger sine strofer et antal gange via metoden syngVers. Et vers består altså af et antal strofer (linier) i en bestemt rækkefølge. Når et vers synges holdes der en pause efter afsyngningen af hver strofe. Pausen's længde er bestemt af instansvariablen forsinkelse. Pausen realiseres ved at tråden sover et tidsrum, som er bestemt af en parameter til konstruktoren. Når en tråd sover kan Java's køretidssystem benytte lejligheden til at give CPU'en til en anden tråd, som kan udføres
class Skjald extends Thread{
  
  private String[] strofer;
  private int forsinkelse;
  private int gentagelser;

  public Skjald(String[] strofer, int forsinkelse, int gentagelser){
    super();
    this.strofer = strofer;
    this.forsinkelse = forsinkelse;
    this.gentagelser = gentagelser;
  }

  private void syngVers(){
    for(int i = 0; i < strofer.length; i++){
      System.out.println(strofer[i]);
      try{
        Thread.sleep(forsinkelse);
      } 
      catch (InterruptedException e) 
        {}
    }
  }

  public void run(){
    for(int g = 1; g <= gentagelser; g++){
      syngVers();
    }
  } // end run

} // end class Skjald

Program: Klassen Sangkor, som starter to aktive Skjald objekter. Hver skjald synger en af vort land's elskede sange, hhv. 'Mester Jakob' og 'Glade jul'. Bemærk at pauserne mellem stroferne i 'Mester Jakob' er væsentlig kortere end i 'Glade jul' (som jo bør afsynges i 'salmetempo').
class Sangkor{
  
  private static String[] mesterJakob = 
    {"MESTER JAKOB, MESTER JAKOB", "SOVER DU, SOVER DU", 
     "HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN", 
     "BIM BAM BUM, BIM BAM BUM"};

  private static String[] gladeJul =
    {"Glade jul, dejlige jul", "Engle daler ned i skjul!",
     "Hid de flyver med paradisgrønt", "Hvor de ser, hvad for Gud er kønt", 
     "Lønligt iblandt os de går", "Lønligt iblandt os de går"};

  public static void main(String[] args){
    Skjald skjald1 = new Skjald(mesterJakob,500,2);
    Skjald skjald2 = new Skjald(gladeJul,3000,2);
 
    skjald1.start();  skjald2.start();
  }
} // end class Sangkor

Program: Det samlede program med klasserne Skjald og Sangkor.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Sangkor.java

Program: Muligt output fra ovenstående program. Vi siger 'muligt output', idet andre skeduleringer ikke kan udelukkes (f.eks. på en anden platform, som implementerer en anden skeduleringsalgoritme)
MESTER JAKOB, MESTER JAKOB
Glade jul, dejlige jul
SOVER DU, SOVER DU
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM
MESTER JAKOB, MESTER JAKOB
SOVER DU, SOVER DU
Engle daler ned i skjul!
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går
Glade jul, dejlige jul
Engle daler ned i skjul!
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går

Mulige tilstande af en tråd i Java
Slide Note Contents Index
References 
En tråd kan være i flere forskellige tilstande. Trådens tilstand er af afgørende betydning for skeduleringen af tråde i et Java system. Skedulering behandles længere fremme i denne lektion

En tråd's tilstand er af betydning, når det skal afgøres hvilken tråd der skal tildeles tid på CPU'en

Figure. Et tilstandsdiagram som viser at en tråd kan være i én af fire mulige tilstande. Endvidere vises de 'transitioner' (begivenheder) som bringer en tråd fra én tilstand til en anden. Denne figur er tilpasset fra en tilsvarende figur i bogen 'Core Java', volume 2

En tråd siges at være i live (alive) hvis den er i en af tilstandene kørbar eller blokeret

Beskrivelse af betingelsen for at være i live er fortolket ud fra beskrivelsen af metoden isAlive i klassen Thread, jf. hosstående reference

Reference

Tråde i forhold til Swing
Slide Note Contents Index
References 
Vi vil her kort indskyde nogle bemærkninger om tråde i Swing

Swing udfører al eventhåndtering i en såkaldt event-dispatch tråd

  • Generelle retningsliner for brug af tråde i Swing

    • Efter at brugergrænsefladen er realiseret skal al forandring af brugergrænsefladen udføres i event-dispatch tråden

    • Der findes særlige metoder til at 'skyde program ind i' event-dispatch tråden:

    • Hvis der udføres lange operationer i event-dispatch tråden bliver brugergrænsefladen ude af stand til at reagere på events

      • Lange operationer bør - om muligt - udfaktoriseres i en ny tråd fra event-dispatch tråden

Reference


Skedulering af tråde i Java

Indbyrdes fremdrift i tråde
Slide Note Contents Index
References 
Vi vil nu se på de mekanismer, som afgør hvilken tråd, der bliver udført på computeren på det givet tidspunkt

Hvilken tråd skal på et givet tidspunkt udføres på en computer med kun én CPU?

The concept skedulering: Kontrollen af den tidslige og indbyrdes ordning af en mængde handlinger i forskellige tråde kaldes skeduleringSkedulering betyder at arrangere eller kontrollere en mængde handlinger i forhold til hinanden i tidslig rækkefølge. I forhold til samtidighed indebærer skedulering beslutningen om hvilken tråd der skal udføres på computerens CPU på et bestemt tidspunkt

  • Mulige skeduleringsteknikker:

    • Round-robin: Tråde aktiveres cyklisk og skiftevis

      • Et retfærdighedsprincip hvor alle tråde regelmæssigt får chancen for at køre

    • Prioriteret skedulering: Højst prioriterede tråde aktiveres først

      • Kan føre til udsultning af lavt prioriterede tråde

      • Prioritering baseret på et estimat af køretid

        • Eksempel: 'shortest job first'

Skedulering af tråde i et Java program er baseret på prioriteter

Hver tråd i et Java program har tilknyttet et prioritetstal

Skedulering baseret på prioriteter i Java
Slide Note Contents Index
References 
Vi ser nu på detaljerne i Java's skeduleringsalgoritme

  • Skedulering af tråde i Java

    • Java udfører altid en af de kørbare tråde, som har højste prioritet

      • Der kan være flere kørbare tråde som alle har højste prioritet

    • En tråd T kører indtil

      • En kørbar tråd S med højere prioritet opstår (ny tråd S, prioritet(S) hæves, prioritet(T) sænkes)

      • T overgiver frivilligt kontrollen med sleep(), yield(), wait() eller lignende

      • T involveres i blokerende input/output

      • T's run metode er kørt til ende

      • På systemer med time slicing: tråden har været kørt i et fastlagt tidsinterval

        • Kontrollen overgives til en tråd med højeste prioritet

    • Preempting: En tråd med højere prioritet vil overtage kontrollen umiddelbart fra en lavere prioriterede tråd

    • Deterministisk: Skeduleringen varetages af en fast algoritme, som ikke forsøger at simulere nogen form for tilfældighed eller uregelmæssighed

At være 'preempting' betyder direkte oversat at 'tage selv forud for andre'. I ovenstående er betydningen, at en tråd, som på en eller anden måde får højere prioritet end alle andre, umiddelbart overtager CPU'en

I klassen Thread findes en række simple metoder, som ændrer på prioriteterne af en tråd. Disse er setPriority og getPriority

References

Eksempel på prioriteter: Prioriterede sange
Slide Note Contents Index
References 
Vi forsøger nu på en konkret måde at illustrerer nogle af ovenstående prioritetsforhold ved at arbejde videre på Skjald eksemplet

Nedenstående eksempel viser (var tiltænkt at vise) at den højst prioriterede tråd vedbliver med at udsulte lavere prioriterede tråde

Program: Klassen Skjald hvor et vers synges uden 'sleep' mellem stroferne. Efter et vers er der mulighed for en pause, hvis forsinkelse er sat til et positivt heltal
class Skjald extends Thread{
  
  private String[] strofer;
  int forsinkelse;
  int gentagelser;

  public Skjald(String[] strofer, int forsinkelse, int gentagelser){
    super();
    this.strofer = strofer;
    this.forsinkelse = forsinkelse;
    this.gentagelser = gentagelser;
  }

  // udskriver ét vers ad gangen med forsinkelse efter verset
  private void syngVers(){
    StringBuffer str = new StringBuffer(100);
    for(int i = 0; i < strofer.length; i++){
      str.append(strofer[i] + "\n");
    } 
    System.out.println(str.toString());
    try{
      if (forsinkelse > 0) Thread.sleep(forsinkelse);
    } 
    catch (InterruptedException e) 
      {}
  }

  public void run(){
    for(int g = 1; g <= gentagelser; g++){
      syngVers();
    }
  }
} // end class Skjald

Program: Klassen Sangkor, som starter to forskelligt prioriterede skjalde (tråde). Skjalden som synger 'Glade Jul' er højere prioriteret end skjalden, der synger 'Mester Jakob'. På grund af skeduleringsreglerne i Java bliver alle 3 vers af 'Glade Jul' derfor sunget før de 3 vers af 'Mester Jakob', jf. nedenstående output af programmet
class Sangkor2{
  
  private static String[] mesterJakob = 
    {"MESTER JAKOB, MESTER JAKOB", "SOVER DU, SOVER DU", 
     "HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN", 
     "BIM BAM BUM, BIM BAM BUM"};

  private static String[] gladeJul =
    {"Glade jul, dejlige jul", "Engle daler ned i skjul!",
      "Hid de flyver med paradisgrønt", "Hvor de ser, hvad for Gud er kønt", 
     "Lønligt iblandt os de går", "Lønligt iblandt os de går"};

  public static void main(String[] args){
    Skjald skjald1 = new Skjald(mesterJakob,0,3);
    Skjald skjald2 = new Skjald(gladeJul,0,3);

    skjald1.setPriority(Thread.NORM_PRIORITY);
    skjald2.setPriority(Thread.NORM_PRIORITY+1);

    skjald1.start();  
    skjald2.start();


  }
} // end class Sangkor2

Program: Det samlede program med klasserne Skjald og Sangkor.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Sangkor2.java

Program: Muligt output fra ovenstående program. Vi siger igen 'muligt output', idet andre skeduleringer stadigvæk ikke kan udelukkes
Glade jul, dejlige jul
Engle daler ned i skjul!
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går

Glade jul, dejlige jul
Engle daler ned i skjul!
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går

Glade jul, dejlige jul
Engle daler ned i skjul!
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går

MESTER JAKOB, MESTER JAKOB
SOVER DU, SOVER DU
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM

MESTER JAKOB, MESTER JAKOB
SOVER DU, SOVER DU
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM

MESTER JAKOB, MESTER JAKOB
SOVER DU, SOVER DU
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM

Skulle det ske (en særlig dag, på et obskurt Java system, ...) at den lavere prioriterede tråd fik køretid på bekostning af den højere prioriterede tråd ville dette ikke være at opfatte som en fejl

Det er meget svært at eftervise skedulerings konsekvenserne af den indbyrdes tråd prioritering i Java

De fleste former for observation påvirker skeduleringen

Viden om egenskaberne ved skeduleringen af tråde må ikke benyttes i forbindelse med ræsonnementer om programmets korrekthed

Den indbyrdes hastighed af trådene i et Java program er grundliggende ukendt

Tommelfingerregler for prioritering
Slide Note Contents Index
References 

Efter hvilke retningslinier skal man tildele prioriteter til tråde?

  • Retningslinier

    • Tråde som skal reagere på brugerinput, f.eks. event listeners, bør tildeles en høj prioritet

    • Tråde med høj prioritet bør ikke varetage opgaver med lang køretid

      • Opgaver med lang køretid bør uddelegeres til en ny tråd af lavere prioritet


Synkronisering af tråde i Java

Interferens mellem tråde
Slide Note Contents Index
References 
Vi vil her studere det forhold, at to tråde let kan komme til at 'træde hinanden over tæerne'

To tråde kan interferere på uheldig vis hvis trådene tilgår fælles data

Figure. Illustration af situationen hvor én konto opdateres samtidig af to forskellige tråde. Hvis der indledningsvist er 100 kroner på kontoen vil der efter de to samtidige opdateringer være 5100 kroner på kontoen. Den grønne opdatering med de 1000 kroner går tabt. Dette er klart en fejl, som ikke kan tolereres

Der er behov for at kunne koordinere tråde med henblik på at forhindre utilsigtet interferens

Kritiske regioner
Slide Note Contents Index
References 
Første skridt på vej mod kontrol af utilsigtet interferens mellem tråde består i at sikre mulighed for udelelig adgang til fælles ressourcer. Det relevante begreb til dette er en 'kritisk region'

Der er behov for at kunne definere områder i et program, som kan udføres af højst én tråd ad gangen

The concept kritisk region: En kritisk region er en sekvens af kommandoer som kan udføres af højst én tråd ad gangenFor at sikre os mod uheldig interferens definerer vi en kritisk region som et område af et program, hvori højst én tråd må befinde sig ad gangen

Synkroniserede metoder
Slide Note Contents Index
References 

I objekt-orienteret programmering er det naturligt at kroppen af bestemte metoder virker som kritiske regioner

Syntax: Syntaks for en synkroniseret metode definition. Hvis en tråd aktiverer en synkroniseret metode låses objektet for andre tråde, således at disse ikke kan udføre metoder på objektet. Ovenstående syntaks viser eksplicit, at vi benytter nøgleordet 'synchronized' som modifier. Ret beset er nøgleordet synchronized en del af metoden's egenskabsliste, side om side med egenskaber så som private og static

egenskabsListe synchronized returType metodeNavn (parametre) 
         throws exceptionType-1, exceptionType-2 ... {   
  metodeKrop
}

Når en synkroniseret metode udføres i en tråd låses objektet for alle andre tråde

Der er således kun én tråd ad gangen, der kan påvirke et objekt hvis alle objektets metoder er synkroniserede

Eksempel på synkronisering: SynkroniseretKonto
Slide Note Contents Index
References 
Som et eksempel på ovenstående vil vi studerende en konto med synkroniserede operationer

Reference

Program: Java klassen SynkroniseretKonto. Alle operationer som aflæser og modificerer egenskaber ved en konto låser nu konto objektet
class SynkroniseretKonto {
   private double rentesats;
   private String navn;
   private double saldo;

   SynkroniseretKonto(String ejer) {
      rentesats = 0.02; 
      navn = ejer; 
      saldo = 0;
   }

   public synchronized double balance () {
      return saldo;
   }

   public synchronized void hæv ( double beløb ) {
      saldo = saldo - beløb;
   }

   public synchronized void indsæt ( double beløb) {
      saldo = saldo + beløb;
   }         

   public synchronized void overførFra(double beløb, SynkroniseretKonto fra) {
      fra.hæv(beløb);
      indsæt(beløb);
   } 

   public synchronized void tilskrivRente() {
      saldo = saldo + saldo * rentesats;
   }

   public synchronized String toString() {
      return navn + "'s konto indeholder "
            + saldo + " kroner";
   }
} // End SynkroniseretKonto

Program: En bank som opstarter to filialer der gennemfører ens transaktioner. Vi benytter her en ganske kompakt måde at definere de to banktråde. I en mere realistisk situation kunne man definere en klasse Filial, som får adgang til en række fælles konto objekter.
class SikkerBank {
 
   public static void main( String[] args ) {

     final SynkroniseretKonto konto1 = new SynkroniseretKonto("Jens");
     final SynkroniseretKonto konto2 = new SynkroniseretKonto("Peter");

     // Filial 1:
     (new Thread () 
           { public void run() {
               konto1.indsæt(100);
               konto2.indsæt(500);
               System.out.println(konto1);  System.out.println(konto2);
            
               konto1.tilskrivRente();
               konto2.hæv(125);
               System.out.println(konto1);  System.out.println(konto2);
            
               konto1.overførFra(25, konto2);
               System.out.println(konto1);  System.out.println(konto2);
              }
           }).start();

     // Filial 2:
     (new Thread () 
           { public void run() {
               konto1.indsæt(100);
               konto2.indsæt(500);
               System.out.println(konto1);  System.out.println(konto2);
            
               konto1.tilskrivRente();
               konto2.hæv(125);
               System.out.println(konto1);  System.out.println(konto2);
            
               konto1.overførFra(25, konto2);
               System.out.println(konto1);  System.out.println(konto2);
              }
           }).start();

   }
} // End SikkerBank

Reference

Program: Det samlede program med SynkroniseretKonto og filial opstart.
/user/normark/courses/prog1/prog1-01/sources/java/babybank/java-oop/SikkerBank.java

Exercise 14.1. Opgave om logging af transaktioner i en bank med filialerI forlængelse af ovenstående eksempel, hvor to filialer i en bank opererer samtidigt på fælles konti, bedes I indføre en BankLogging klasse, som skal holde styr på følgende:
    Hvilke operationer der udføres på de forskellige konti i banken Klokkeslæt for gennemførelsen af en transaktion Rækkefølgen af transaktionerne
En instans af klassen BankLogging overføres til alle Konto objekter. Når der gennemføres en transaktion på en konto registreres forskellige data om transaktionen i loggen.

Vi kan bl.a. bruge denne log til at finde ud af i hvilken rækkefølge operationerne udføres fra de to samtidigt udførende filialer i eksemplet ovenfor.

Hvad angår tidsbehandling henvises til metoden getTime mv. i klassen java.util.Calendar.

Detaljer om synkroniserede metoder
Slide Note Contents Index
References 
Vi giver her en række detaljer om synkroniserede metoder i Java

  • Synkroniserede metoder:

    • En synkroniseret metode låser ét objekt, ikke alle objekter i en klasse

    • En synkroniseret metode er mere end blot definitionen af metodens krop som kritisk region

      • Ingen andre tråde kan tilgå nogen metode i objektet

    • En synkroniseret metode kan aktivere en anden synkroniseret metode i samme objekt

      • 'Locks are reentrant'

    • Det er unødvendigt at synkronisere konstruktorer

    • Det er tilladt at redefinere en synkroniseret metode med en ikke synkroniseret metode

    • Statiske metoder kan være synchronized

      • Sikrer gensidig udelukkelse af andre statiske metoder på klassen

      • Låser ingen instanser af klassen

Årsagen til, at kontruktorer ikke skal synkroniseres er, at objektet skal skabes inden der er behov for konflikter ved samtidig tilgang. Og vi er netop igang med at skabe (initialisere) objektet i en konstruktor

Trådsikre klasser
Slide Note Contents Index
References 
Man taler ofte om trådsikre klasse biblioteker i forbindelse med programmeringssprog, som understøtter flere samtidige programforløb. Det engelske ord er 'thread safeness'

The concept trådsikker klasse: En klasse siges at være trådsikker hvis den er beskyttet mod multipel og samtidig tilgang fra flere trådeVi siger at en klasse er trådsikker (på engelsk 'thread safe') hvis den er beskyttet mod multipel og samtidig tilgang fra flere tråde. Beskyttelsen består i at hindre flere tråde i samtidig tilgang, hvor der opstår konflikt mellem to opdateringer, mellem en opdatering og en aflæsning, eller lignende

  • Trådsikkerhed i Java bibliotekerne:

    • Hvis alle metoder var synkroniserede ville vi drastisk reducere graden af samtidigehed

    • Det er en tendens i Java bibliotekerne at færre metoder synkroniseres

Java's synchronized kommando
Slide Note Contents Index
References 
Java's synchronized kommando låser et objekt uden brug af låsemekanismen i synkroniserede metoder

Der findes en kommando i Java som låser et objekt for samtidig tilgang, på samme måde som synkroniserede metoder låser objektet, hvori de synkroniserede metoder hører til

Syntax: Syntaks for en synkroniseret blok. Udtrykket evalueres. Resultatet er et objekt som låses for samtidig adgang fra andre tråde under udførelse af blokken

synchronized (udtryk) 
  blok

  • Anvendelser af synchronized

    • Blokering af ikke-trådsikker ressource i anvendelsessituationen i modsætning til definitionssituationen

      • Eksempel på næste slide

    • Introduktion af et låse objekt, som synkroniserer tilgang til en række kommandoer

      • Objektet benyttes ikke nødvendigvis i kommandoerne

Generelt forekommer det, at anvendelse af en synchronized kommando er en dårlig løsning i forhold til at bruge synchronized metoder i en klasse. Eksemplet på næste slide illustrerer denne påstand

Ved at introducere det låse objekt, hvorpå der synkroniseres med en synchronized kommando, kan man sikre udelelig adgang til en ressource fra blokke i flere tråde. Hver tråd skal blot bruge en synchronized kommando på objektet. Synkroniseringskommandoen og objektet tjener som en indgang til ressourcen, der samtidig låser for andre trådes tilgang

Reference

Synkroniserede konti via synchronized kommandoer
Slide Note Contents Index
References 
Vi gentager ovenstående synkroniseret konto eksempel, nu blot vist med synchronized kommandoer i stedet for synkroniserede metoder

References

Program: Java klassen Konto. Denne klasse svarer til vores oprindelige Konto klasse, som vi udviklede i en af de første lektioner i dette kursus.
class Konto {
   private double rentesats;
   private String navn;
   private double saldo;

   public Konto(String ejer) {
      rentesats = 0.02;
      navn = ejer; 
      saldo = 0;
   }

   public double balance () {
      return saldo;
   }

   public void hæv (double beløb) {
      saldo = saldo - beløb;
   }

   public void indsæt (double beløb) {
      saldo = saldo + beløb;
   }         

   public void tilskrivRente() {
      saldo = saldo + saldo * rentesats;
   }

   public void overførFra(double beløb, Konto fra) {
      fra.hæv(beløb);
      indsæt(beløb);
   } 

   public String toString() {
      return navn + "'s konto indeholder "
            + saldo + " kroner";
   }
} // End Konto

Program: En bank som opstarter to filialer der gennemfører ens transaktioner. Transaktionerne her er lidt anderledes end i det tilsvarende eksempel ovenfor. Stadig udfører hver 'filial' ens transaktioner (hvilket naturligvis er lidt sært, men let for programmøren af denne klasse). Vi ser et væld at synkroniseringer på konto1 hhv. konto2 for at sikre mod samtidige opdateringer i de to filialer
class SikkerBank1 {
 
   public static void main( String[] args ) {

     final Konto konto1 = new Konto("Jens");
     final Konto konto2 = new Konto("Peter");

     // Filial 1:
     (new Thread () 
           { public void run() {
               synchronized(konto1){
                 konto1.indsæt(100);
               }
               synchronized(konto2){
                 konto2.indsæt(500);
               }

               synchronized(konto1){            
                 konto1.tilskrivRente();
               }
               synchronized(konto2){
                 konto2.hæv(125);
               }

               synchronized(konto1){            
                 konto1.overførFra(25, konto2);
               }

               synchronized(konto1){ 
                 System.out.println(konto1);
               }
               synchronized(konto2){
                 System.out.println(konto2);
               }
             }
           }).start();

     // Filial 2:
     (new Thread () 
           { public void run() {
               synchronized(konto1){
                 konto1.indsæt(100);
               }
               synchronized(konto2){
                 konto2.indsæt(500);
               }

               synchronized(konto1){            
                 konto1.tilskrivRente();
               }
               synchronized(konto2){
                 konto2.hæv(125);
               }

               synchronized(konto1){            
                 konto1.overførFra(25, konto2);
               }

               synchronized(konto1){ 
                 System.out.println(konto1);
               }
               synchronized(konto2){
                 System.out.println(konto2);
               }
             }
           }
           ).start();

   }
} // End SikkerBank1

Program: Det samlede program med Konto og 'filial opstart'.
/user/normark/courses/prog1/prog1-01/sources/java/babybank/java-oop/SikkerBank1.java

Det er væsentlig mere omstændeligt at bruge synchronized kommandoer i anvendelsessituationen end synkroniserede metoder i definitionssituationen

Monitorer
Slide Note Contents Index
References 
Hvis man interesserer sig for samtidige programforløb er monitorer er et vigtigt begreb. Selv om vi reelt allerede har dækket essensen af begrebet gør vi det her monitorbegrebet eksplicit

Monitorer er det underliggende begreb af klasser med synkroniserede metoder

The concept monitor: En klasse med synkroniserede metoder er også kendt som en monitor. Metoderne udgør kritiske regioner. Internt er der tilknyttet en kø af ventende tråde til et monitor objektMonitorer blev oprindelig beskrevet af Hoare (som hører til pionererne i faget). Vi definerer her en monitor som en en klasse med synkroniserede metoder. Dette er ikke den oprindelige definition (alene af den grund, at monitorer blev beskrevet i et ikke-objekt-orienteret programmeringssprog). Men det ville være bagvendt her at indføre en mere basal definition, der i et og alt leder frem til det sammen som den her givne.

Java anvender monitorer til at forklare låsemekanismen, som kan være knyttet til objekter i Java

Java's beskrivelse af monitorer kan bl.a. findes i klassen Object, ved metoderne notify og wait, jf. nedenstående reference til API dokumentationen i Java Development Kit

Reference

Synkronisering: For lidt og for meget
Slide Note Contents Index
References 
Vi dvæler her ved retningslinier for hvor store programdele der bør findes i synkroniserede metoder

Anvendelse af synkronisering er en balancegang mellem 'for lidt' og 'for meget'

  • For lidt synkronisering:

    • To tråde 'træder hinanden over tæerne'

    • Inkonsistens pga. af samtidig adgang til fælles ressourcer

  • For meget synkronisering:

    • To tråde tvinges reelt til at blive udført sekventielt

    • Dårlig respons idet hændelser i den ene tråd ikke har en chance for at blive håndteret

Når vi siger 'for meget synkronisering' tænker vi på situationen hvor to eller flere tråde udføre store dele af tråden inden for en synkroniseret metode. Derved er det objekt, hvor metoden er placeret, låst. Hermed kan ingen andre udføre metoder på objektet

Synkroniserede metoder bør lige netop beskytte fælles ressourcer for udelelig tilgang

Udførelse af andre programdele inden for rammerne af synkroniserede metoder er upassende

Eksempel på et program med synkronisering: Producent og Forbruger
Slide Note Contents Index
References 
Producent Forbruger eksemplet er klassisk hvad angår illustrationen af to tråde eller processer, som skal samarbejde på en synkroniseret måde. Eksemplet er også meget relevant i den praktiske verden

Der opstår ofte et behov for at synkronisere to tråde endnu mere end blot ved 'gensidig udelukkelse' på fælles objekter

Et sådant behov opstår hvis to tråde skal samarbejde om udførelse af en opgave

Eksempel. 'Producent Forbruger' er et klassisk eksempel på et samarbejde mellem to processer.

Producenten fremstiller objekter, som sendes til forbrugeren.

Forbrugeren modtager objekter, og benytter dem til et bestemt formål

 

Program: Producent klassen. Denne klasse laver objekter (her blot tal) som skal afleveres til forbruger objektet. Når denne anstrengelse er overstået er der behov for at hvile ud - samle kræfter - så næste ting kan produceres (sleep).
class Producer extends Thread {
    private CubbyHole cubbyhole;
    private int number;

    public Producer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            cubbyhole.put(i);
            try {
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
} // end Producer

Program: Consumer klassen. Denne klasse forbruger de objekter, der modtages fra producent klassen. Forbruget består her i blot at udskrive objekterne (tal)
class Consumer extends Thread {
    private CubbyHole cubbyhole;
    private int number;

    public Consumer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    public void run() {
        int value = 0;
        for (int i = 0; i < 10; i++) {
            value = cubbyhole.get();
            try {
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
} // end Consumer

Program: En simpel udgave af cubbyhole klassen, som ikke virker. Denne udgave er simplificeret helt ind til benet. Vi medtager alene denne simple udgave af postkassen for at motivere os selv til at lave en, som faktisk virker.
class CubbyHole {
    private int contents;

    public synchronized int get() {
        System.out.println("Consumer got: " + contents);
        return contents;
    }

    public synchronized void put(int value) {
        contents = value;
        System.out.println("Producer put: " + value);
    }
} // end CubbyHole

Program: Klasse med en statisk main metode, der laver CubbyHole, og igangsætter Producer og Consumer.
class ProducerConsumerTest {
    public static void main(String[] args) {
        CubbyHole c = new CubbyHole();
        Producer p1 = new Producer(c, 1);
        Consumer c1 = new Consumer(c, 1);

        p1.start();
        c1.start();
    }
} // end ProducerConsumerTest

Program: Output fra kørsel af programmet ovenfor, som illustrerer problemet. Vi ser at tallet 0 bliver produceret og forbrugt korrekt. Men 2 bliver produceret for hurtigt, og således at 1 aldrig bliver forbrugt. Det samme gør sig gældende for 3, osv.
Producer put: 0
Consumer got: 0
Producer put: 1
Producer put: 2
Consumer got: 2
Producer put: 3
Producer put: 4
Consumer got: 4
Producer put: 5
Consumer got: 5
Consumer got: 5
Producer put: 6
Producer put: 7
Consumer got: 7
Producer put: 8
Producer put: 9
Consumer got: 9
Consumer got: 9
Consumer got: 9
Consumer got: 9

Program: En forbedret CubbyHole klassen. Der kan i dette eksempel kun opbevares ét objekt i postkassen. Det er vigtigt at forstå, at vi ikke kender til rækkefølgen af kald til put og get. Hvis put kaldes først (af producenten) skal vi sikre os, at vi ikke overskriver et allerede eksisterende objekt (contents). Den boolske variable available er falsk hvis der er plads til et objekt i Cubbyhole. Hvis get kaldes først (af forbrugeren) bliver vi nødt til at vente på at forbrugeren skal putte noget i postkassen. While løkken i get kan forstås som 'afvent at der noget i postboksen' (altså available er true). While løkken i put kan forstås som 'afvent at postboksen er tom' (altså available er false). Bemærk brugen af while (idiomatisk anvendelse, jf. efterfølgende slide).
class CubbyHole {
    private int contents;
    private boolean available = false;

    public synchronized int get() {
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) { }
        } // while
        available = false;
        System.out.println("Consumer got: " + contents);

        notifyAll();
        return contents;
    }

    public synchronized void put(int value) {
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) { }
        } // while
        contents = value;
        available = true;
        System.out.println("Producer put: " + value);
        notifyAll();
    }
} // end CubbyHole

Program: Output fra kørsel af programmet ovenfor.
Producer put: 0
Consumer got: 0
Producer put: 1
Consumer got: 1
Producer put: 2
Consumer got: 2
Producer put: 3
Consumer got: 3
Producer put: 4
Consumer got: 4
Producer put: 5
Consumer got: 5
Producer put: 6
Consumer got: 6
Producer put: 7
Consumer got: 7
Producer put: 8
Consumer got: 8
Producer put: 9
Consumer got: 9

Klasserne vist ovenfor stammer direkte fra et eksempel i The Java Tutorial, jf hosstående henvisning

Reference

Wait og notify
Slide Note Contents Index
References 
Wait og notify er primitiverne til som tillader os at programmere synkroniserede tråde. Vi ser her nærmere på disse primitiver

Wait og notify, og varianter af disse, er metoder i klassen Object

Det betyder at ethvert objekt umiddelbart kan aktivere wait og notifiy

References

  • Wait

    • Skal forekomme i en synkroniseret metode i det objekt, hvori andre tråde kan kalde notify

    • Bringer tråden i en blokeret tilstand, hvor det venter på et notify signal

    • Frigiver låsen på det objekt hvori wait forekommer

    • Overgiver kontrollen til et andet objekt via mekanismerne i skedulerings algoritmen

    • Varianterne wait(long) og wait(long,int) venter højst i et tidsrum, der er angivet af parametrene

  • Notify

    • Skal forekomme i en synkroniseret metode i det objekt, hvori andre tråde kan kalde wait

    • Vækker højst én tråd, som venter i dette objekt

    • Udfører dog resten af den synkroniserede metode inden vækningen

    • Varianten NotifyAll giver alle tråde, som venter, en chance for at komme igang. - Det lykkes imidlertid kun for én af de ventende

Wait og Notify idioms
Slide Note Contents Index
References 
Der kan anvises nogle typiske mønstre og regler for anvendelse af wait og notify til synkronisering af samarbejdende tråde. Vi ser her nærmere på disse 'idioms'

Wait skal altid kaldes i en betingelses-afventende løkke

Notify skal altid kaldes når betingelsen kan være ændret

Program: Typiske mønstre for anvendelse af wait of notify metoderne i en klasse, der virker som en monitor.
class MonitorClass {

  condition variable;

  synchronized void doWhenCondition(){
    while (!condition){
      wait();
    }
    // Betingelsen er opfyldt
    // Udfør handlinger som har afventet betingelsen
  }

  synchronized void changeCondition(){
    // Modificer en variabel, som indgår i betingelsen
    notifyAll();
  }
}

  • Metoden wait skal altid kaldes i en løkke, ikke blot betinget af en if

    • At blive vækket gennem notify er ikke ensbetydende med at wait's betingelse er opfyldt

  • Når et synkroniseret objekt skifter tilstand bør den altid kalde metoden notifyAll

    • Derved får ventende tråde chancen for at checke om der er sket det tilstandsskift, som muliggør en tråds genoptagelse

Eksempel på et program med synkronisering: Synkroniseret Sangkor
Slide Note Contents Index
References 
Vi vender nu tilbage til sangkor eksemplet. I vores første møde med sangkoret blev de enkelte strofer sunget hulter til bulter. I det næste eksempel justerede vi på prioriteterne, således at den ene sang blev sunget før den anden. Dette var dog ikke nogen garanteret egenskab, men blot et udfald af skeduleringsalgoritmens virkemåde. Nu ønsker vi at lave en version af sangkoret, som skiftevis synger et vers af den ene og den anden sang. (Stadig gentager vi blot samme vers flere gange). Versionen, som vi udvikler her skal garantere, at den skiftevise rækkefølge mellem versene opnås

Eksempel. I SangKor eksemplet ønsker vi nu en synkroniseret opførsel, således at der skiftevis lyder et vers fra hver sang
 

Program: Klassen Skjald som på disciplineret vis indtager og frigiver scenen.
class Skjald extends Thread{
  
  private String[] strofer;
  int forsinkelse;      // pause efter et vers
  int gentagelser;      // hvor mange gange synger jeg verset
  int mig;              // mit nummer
  Dirigent coordinator; // reference til dirigenten

  public Skjald(int mig, Dirigent coordinator, String[] strofer, 
                int forsinkelse, int gentagelser){
    super();
    this.mig = mig;
    this.coordinator = coordinator;
    this.strofer = strofer;
    this.forsinkelse = forsinkelse;
    this.gentagelser = gentagelser;
  }

  // udskriver ét vers ad gangen:
  private void syngVers(){
    coordinator.tagScenen(mig);
    StringBuffer str = new StringBuffer(100);
    for(int i = 0; i < strofer.length; i++){
      str.append(strofer[i] + "\n");
    } 
    System.out.println(str.toString());
    try{
      if (forsinkelse > 0) Thread.sleep(forsinkelse);
    } 
    catch (InterruptedException e) 
      {};
    coordinator.frigivScenen();
  }

  public void run(){
    for(int g = 1; g <= gentagelser; g++){
        syngVers();
    }
    // jeg burde måske her give den anden lov til at synge færdig
  }
} // end class Skjald

Program: Klassen Dirigent som synkroniserer to Skjalde. Betingelsen i while løkken siger at scenen enten ikke er ledig eller at det er den anden Skjald's tur. Under disse omstændigheder skal der ventes
class Dirigent{
  
  boolean scenenLedig;
  int næsteSanger;

  public Dirigent(int hvemStarter){
    scenenLedig = true; 
    næsteSanger = hvemStarter;
  }

  private int enAndenEnd(int i){
    if (i == 1) return(2);
    else if (i == 2) return(1);
    else return(0);
  }

  public synchronized void tagScenen(int hvemØnskerAtSynge){
   while (!scenenLedig || hvemØnskerAtSynge != næsteSanger){
    try {
      wait();
    } catch (InterruptedException e) { }
   } // end while
   // scenenLedig && hvemØnskerAtSynge == næsteSanger.
   scenenLedig = false;
   næsteSanger = enAndenEnd(hvemØnskerAtSynge);
  }

  public synchronized void frigivScenen(){
   scenenLedig = true;
   notifyAll();
  }

} // end class Dirigent

Program: Klassen Sangkor3, som starter to Skjalde og en Dirigent.
class Sangkor3{

  static String[] mesterJakob = 
    {"MESTER JAKOB, MESTER JAKOB", "SOVER DU, SOVER DU", 
     "HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN", 
     "BIM BAM BUM, BIM BAM BUM"};

  static String[] gladeJul =
    {"Glade jul, dejlige jul", "Engle daler ned i skjul!",
      "Hid de flyver med paradisgrønt", "Hvor de ser, hvad for Gud er kønt", 
     "Lønligt iblandt os de går", "Lønligt iblandt os de går"};


  public static void main(String[] args){
    Dirigent jan_Glæsel = new Dirigent(2);
   
    // virker bedst når der er lige mange vers
    Skjald skjald1 = new Skjald(1, jan_Glæsel, mesterJakob, 1000, 2);
    Skjald skjald2 = new Skjald(2, jan_Glæsel, gladeJul, 200, 2);

    skjald1.start();
    skjald2.start();  
  }
} // end class Sangkor3

Program: Output fra ovenstående program. Som det var ønsket ser vi at vers fra de to sange synges på skift.
Glade jul, dejlige jul
Engle daler ned i skjul!
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går

MESTER JAKOB, MESTER JAKOB
SOVER DU, SOVER DU
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM

Glade jul, dejlige jul
Engle daler ned i skjul!
Hid de flyver med paradisgrønt
Hvor de ser, hvad for Gud er kønt
Lønligt iblandt os de går
Lønligt iblandt os de går

MESTER JAKOB, MESTER JAKOB
SOVER DU, SOVER DU
HØRER DU EJ KLOKKEN, HØRER DU EJ KLOKKEN
BIM BAM BUM, BIM BAM BUM

Program: Det samlede program med klasserne Sangkor3, Skjald og Dirigent.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Sangkor3.java

Exercise 14.2. Opgave om synkroniserede skjaldeI forlængelse af ovenstående eksempel kan man observere, at hvis den ene sang synges (f.eks.) 2 gange, og den anden sang 4 gange kører det samlede Java program aldrig til ende.

    Studer programmet og overbevis dig selv om årsagen til dette problem
Indfør nu en forandring i programmet, således at 'rest vers' fra den lange sang bliver sunget efter at den korte sang er sunget til ende

Skedulerende og synkroniserende metoder: oversigt
Slide Note Contents Index
References 
Der er andre skedulerende og synkroniserende primitiver end wait og notify. Vi ser her på et bredt udvalg, som næsten kommer omkring alle relevante metoder til formålene

  • Metoden wait i klassen Object

    • Afventer en notify besked

  • Metoden notify i klassen Object

    • Igangsætter en ventende tråd

  • Metoden suspend i klassen Thread (deprecated)

    • Sætter en levende tråd i stå og afventer resume

  • Metoden resume i klassen Thread (deprecated)

    • Igangsætter en levende, men suspenderet tråd

  • Metoden sleep i klassen Thread

    • Statisk metode som midlertidigt stopper den kørende tråd

  • Metoden yield i klassen Thread

    • Holder frivilligt en pause således at en anden tråd kan få køretid

  • Metoden join i klassen Thread

    • Afventer at modtageren af join beskeden dør

  • Metoden start i klassen Thread

    • Starter en tråd (eller rettere, bringer tråden i en kørbar tilstand)

  • Metoden stop i klassen Thread (deprecated)

    • Stopper en tråd, som bringes i tilstanden 'død'

Reference


Trådgrupper

Trådgrupper
Slide Note Contents Index
References 
I Java kan tråde organiseres i grupper (thread groups), som selv kan indeholde trådgrupper. På denne måde kan tråde i Java struktureres i et hierarki, hvis niveauer på flere måder kan organiseres under ét. Trådgrupper er først og fremmest relevante hvis man har mange tråde i et Java program

Hvis man har mange tråde i et program kan disse med fordel organiseres hierarkisk

En hierarkisk organisering tillader os på en enkel måde at manipulere en delmængde af tråde som en helhed

The concept trådgruppe: En trådgruppe er en mængde af tråde, som rekursivt kan indeholde andre trådgrupperEn trådgruppe definerer et træ hvor bladene er tråde. De indre knuder er trådgrupper

Reference

  • Udvalgte egenskaber af trådgrupper

    • Default gruppen, hvori alle tråde placeres hvis ikke andet angives, hedder main

    • Tråde kan ikke flyttes mellem grupper

    • Alle tråde i en gruppe kan stoppes, suspenderes og genoptages ved et sende én besked til gruppen

    • Prioriteten af (kommende tråde) kan styres ved at sætte gruppens maksimumprioritet

    • Enumerering af alle aktive tråde til et array


Collected references
Contents Index
The Java Tutorial: 'Doing Two or More Tasks at Once: Threads'
The reference above goes to a Java Tutorial pageThe references above goes to material on the Internet
Interfacet Runnable i pakken java.lang
The reference from above goes to Java API pageThe references above goes to material on the Internet
Klassen Thread i pakken java.lang
The reference from above goes to Java API pageThe references above goes to material on the Internet
Metoden isAlive i klassen Thread
The reference from above goes to Java API pageThe references above goes to material on the Internet
Threads and Swing
The reference above goes to a Java Tutorial pageThe references above goes to material on the Internet
Metoden setPriority i klassen Thread
The reference from above goes to Java API pageThe references above goes to material on the Internet
Metoden getPriority i klassen Thread
The reference from above goes to Java API pageThe references above goes to material on the Internet
Klassen Konto
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Singulært definerede klasser
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Blokke
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Klassen SynchronizedKonto fra denne lektion
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Beskrivelse af monitorer i klassen Object
The reference from above goes to Java API pageThe references above goes to material on the Internet
Producer Consumer eksemplet fra The Java Tutorial
The reference above goes to a Java Tutorial pageThe references above goes to material on the Internet
Tråd tilstande
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Metoden notify i klassen Object
The reference from above goes to Java API pageThe references above goes to material on the Internet
Metoden wait i klassen Object
The reference from above goes to Java API pageThe references above goes to material on the Internet
Klassen Object i pakken java.lang
The reference from above goes to Java API pageThe references above goes to material on the Internet
Tråd tilstande og levende tråd
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Klassen ThreadGroup i pakken java.lang
The reference from above goes to Java API pageThe references above goes to material on the Internet

 

Chapter 14: Samtidighed i Java
Course home     Author home     About producing this web     Previous lecture (top)     Next lecture (top)     Previous lecture (bund)     Next lecture (bund)     
Generated: March 31, 2008, 12:09:55