Dagens program Grunnleggende tråd-programmering i Java: Java-tråder: definisjoner, egenskaper Interferens Låsing og synkronisering Venting og signallering Løpende eksempel: lesere og skrivere Erfaringer og høynivå-konstruksjoner: Vranglås, problemer med leksikalsk låsing Høynivå-konstruksjoner for samtidig programmering (Java 5.0) Eksempel: begrenset buffer INF3140 / 2007-10-17 Samtidig programming i Java 1 Operativsystemer, prosesser og tråder Operativsystemer (OSer) lar et antall prosesser kjøre på en maskin vha. preemptiv skedulering. Multiprosessorer-maskiner eller multicore -prosessorer: prosesser kan også kjøre i ekte parallell. Tråd: prosess som er billig å lage for OSet, typisk fordi ressurser deles med prosessen som lagde tråden. Ressursdeling åpner for effektiv interaksjon mellom tråder, men kan også føre til interferens mellom tråder. Java og Java Virtual Machine (JVM) uavhengig av OS = tråder må følge visse regler. Java-tråder er integrert i språket. INF3140 / 2007-10-17 Samtidig programming i Java 2
Konkret definisjon av Java-tråder Klassen java.lang.thread public class Thread extends Object implements Runnable { // konstruktorer Thread() {... Thread(Runnable target) {... // metoder public void run() { // re-def.es i subklasser public void start()... // mye utelatt // et relatert grensesnitt public interface Runnable { public void run(); INF3140 / 2007-10-17 Samtidig programming i Java 3 Grunnleggende egenskaper ved Java-tråder Hver tråd kontrolleres av ett unikt Thread-objekt disse to kan betrakes som én. Livssyklus for en tråd: Et objekt av klassen Thread opprettes Livet (utførelsen) begynner når metoden run() kalles Tråden dør når run() avslutter, enten ved retur eller et unntak (eng. exception) Tråder kommuniserer typisk med hverandre ved delt tilstand: Lese/skrive felter i objekter trådene kan referere Lokale variable og metode-parametre deles ikke mellom tråder i Java INF3140 / 2007-10-17 Samtidig programming i Java 4
Egendefinert klasse: Tråder i Java: Et første eksempel class Simple extends Thread { public void run() { System.out.println("Hello threaded world!"); Bruk av klassen: Simple s = new Simple(); s.start(); Metoden start() klargjør objektet for eksekvering. Når prosessoren blir tilgjengelig kaller den s.run(). INF3140 / 2007-10-17 Samtidig programming i Java 5 Eksempel: interferens og Java-tråder Må unngå interferens ved samtidig utførelse av Java-tråder. Gitt følgende kodesnutt: class Store { private int data = 0; public void update() { data++;... // i en metode: Store s = new Store(); // trådene under har tilgang til s t1 = new FooThread(s); t1.start(); t2 = new FooThread(s); t2.start(); Trådene t1 og t2 kan gjøre s.update() samtidig! Interferens mellom t1 og t2 = kan miste oppdateringer av data. INF3140 / 2007-10-17 Samtidig programming i Java 6
Låser og synkronisering Tråder synkroniserer adgang til delte data for å unngå interferens. Mekanisme: 1. Én unik lås (eng. lock) for hvert objekt a o. 2. Kun én tråd T kan låse o til enhver tid. 3. T gjør en forespørsel om å låse o for å utføre blokken (setningene) B slik: synchronized (o) { B T blokkerer (stopper opp) til låsingen lykkes. 4. Først når (hvis) Ts forespørsel lykkes så låses o og T utfører B. 5. Flere tråder kan konkurrere om å låse et objekt. Implementasjonsavhengig rekkefølge for innvilging av låse-forespørsler. 6. T kan låse o på ny fra B (eng. reentrance). 7. Når T avslutter B, eller kaster unntak, slippes låsen på o. a Egentlig: objekt, tabell og klasse. INF3140 / 2007-10-17 Samtidig programming i Java 7 Tilstandsmodell for Java-tråder: låsing foresp. om o new fikk låse o synchronized (o) preempt et laget start() klar skedulert kjører Låsen på o slippes når tråden forlater synchronized-blokken (må skje i tilstand kjører ). Noen tilstander og tråd-primitiver utelatt. a a Hentet fra boka Java Precisely av Peter Sestoft (MIT Press, 2002 & 2005). Gammel versjon fritt tilgjengelig fra http://www.dina.dk/~sestoft/javaprecisely/ INF3140 / 2007-10-17 Samtidig programming i Java 8
Eksempelet del II: ingen interferens Løsning på tidligere problem: låse Store-objekter før utførelse av problematisk metode: class Store { private int data = 0; public void update() { synchronized (this) { data++;... // i en metode: Store s = new Store(); Nå kan kun én tråd av gangen utføre s.update(). Kortform: class Store { private int data = 0; public synchronized void update() { data++; INF3140 / 2007-10-17 Samtidig programming i Java 9 Eksempel: Sekvensielle lesere og skrivere class RWbasic { protected int data = 0; // the database public void read() { System.out.println("read: " + data); public void write() { data++; System.out.println("wrote: " + data); class Main { static RWbasic RW = new RWbasic(); public static void main(string arg) { for (int i = 0; i < 10; i++) { RW.read(); RW.write(); INF3140 / 2007-10-17 Samtidig programming i Java 10
Eksempel: Parallelle (usynkroniserte) lesere og skrivere class Reader extends Thread { RWbasic RW; // Som før public Reader(RWbasic RW) { this.rw = RW; public void run() { for (int i = 0; i < 10; i++) RW.read(); class Writer... // som Reader, men gjør RW.write() i løkka class Main { static RWbasic RW = new RWbasic(); public static void main(string arg) { new Reader(RW).start(); new Writer(RW).start(); INF3140 / 2007-10-17 Samtidig programming i Java 11 Eksempel: Utelukkende lesere og skrivere Synkroniserte metoder gir gjensidige utelukkelse. class RWexclusive extends RWbasic { public synchronized void read() { super.read(); public synchronized void write() { super.write(); dvs. gjenbruker koden fra klassen RWbasic. Klassene Reader, Writer og Main bruker nå RWexclusive isf. RWbasic. Ingen interferens, men... Problem: Maksimalt én leser av gangen. INF3140 / 2007-10-17 Samtidig programming i Java 12
Metoder for venting og signallering Klassen java.lang.object public class Object { // metoder public final void wait() throws InterruptedException {... public final void notify() {... public final void notifyall() {...... // mye utelatt En tråd som kaller metodene på objekt o må ha låst o på forhånd, typisk synchronized (o) {... o.wait():... INF3140 / 2007-10-17 Samtidig programming i Java 13 Forklaring av vente-relaterte metoder For vilkårlig Java-objekt o er følgende definert: o.wait(): slipp låsen på o, gå inn i os ventekø og vent o.notify(): vekk én tråd i os ventekø o.notifyall(): vekk alle tråder i os ventekø Objekter som venter på o (o.wait()) ligger i en kø. Ventekø-ordningen er implementasjonsavhengig. Metodene notify()/notifyall() har Signal and Continue -semantikk. Merk: En tråd som våkner fra o.wait() må låse o påny før den fortsetter. En vekket tråd har ingen fortrinn i konkurransen om låsen til o. INF3140 / 2007-10-17 Samtidig programming i Java 14
Tilstandsmodell for Java-tråder: låsing og venting new laget fikk låse o start() foresp. om o klar o.notify eller o.notifyall interrupt() synchronized (o) preempt et skedulert venter på o kjører o.wait() Det er to uavhengige steder tråder kan blokkere per objekt o: Ved låsing av o: synchronized (o) { B Ved venting på o: o.wait() INF3140 / 2007-10-17 Samtidig programming i Java 15 Eksempel: Ekte lesere og skrivere class ReadersWriters extends RWbasic { privat int nr = 0; private synchronized void startread() {nr++; private synchronized void endread() { nr--; if (nr==0) notify(); // awaken waiting Wrs. public void read() { // not synchronized startread(); super.read(); endread(); public synchronized void write() { while (nr>0) try { wait(); catch (InterruptedException ex) { return; super.write(); notify(); // awaken another waiting Writer Klassene Reader, Writer og Main bruker nå ReadersWriters isf. RWbasic. Problem: Løsningen gir preferanse til lesere. INF3140 / 2007-10-17 Samtidig programming i Java 16
Vranglås T 1 : T 2 : synchronized (a) { synchronized (b) {...... synchronized (b) { synchronized (a) {............ Vranglås (eng. deadlock) hvis T 1 låser a og så T 2 låser b. INF3140 / 2007-10-17 Samtidig programming i Java 17 Leksikalsk (blokkvis) låsing Dvs. låsing vha. synchronized: Fordeler: Automatisk opplåsing ved slutten av synchronized-blokken Oversiktlig, lett å se når en lås holdes Ulemper: Ved låsing av flere objekter: låsene må slippes i motsatt rekkefølge Låser må slippes i samme leksikalske skop som de ble låst INF3140 / 2007-10-17 Samtidig programming i Java 18
Høynivå-konstruksjoner for samtidig programmering Siste versjon av Java (5.0) støtter samtidig programmering på høyt nivå i pakkene java.util.concurrent*. Motivasjon for høynivå-støtten: Mer fleksibel og/eller abstrakt enn synchronized Effektive implementasjoner, bruker ikke synchronized direkte Korrekte implementasjoner av synkroniseringsprimitiver, f.eks. semaforer (ofte en ikke-triviell oppgave!) INF3140 / 2007-10-17 Samtidig programming i Java 19 Fleksible låser med betingelser Grensesnittet java.util.concurrent.locks.lock public interface Lock { // metoder public void lock(); public void unlock(); public boolean trylock(); public Condition newcondition();... // utelatt Grensesnittet java.util.concurrent.locks.condition public interface Condition { public void await() throws InterruptedException; public void signal(); public void signalall();... // utelatt INF3140 / 2007-10-17 Samtidig programming i Java 20
Standardimplementasjon og semantikk Tilbys som standard: Klassen java.util.concurrent.locks.reentrantreadwritelock public class ReentrantReadWriteLock extends Object implements ReadWriteLock,... {... Semantikken til fleksible låser Samme antall kall må gjøres på lock() og unlock() for hver lås Metodene i Condition har Signal and Continue -oppførsel INF3140 / 2007-10-17 Samtidig programming i Java 21 Bruk av fleksible låser Lock l =...; l.lock(); try {... // aksesser ressursen beskyttet av låsen finally { l.unlock(); Kode som utføres når låsen holdes bør beskyttes av try-finally e.l. for å sikre at låsen slippes ved avbrudd. En betingelse assosiert med en lås lages slik: Lock l =...; Condition c = l.newcondition(); Låsen må holdes når betingelsen brukes. INF3140 / 2007-10-17 Samtidig programming i Java 22
Eks.: begrenset buffer vha. Condition class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notfull = lock.newcondition(); final Condition notempty = lock.newcondition(); final Object[] buf = new Object[100]; int rear, front, count; // int defaults to 0 public void deposit(object x) throws InterruptedExc. { lock.lock(); try { while (count == buf.length) notfull.await(); buf[rear] = x; rear = (rear + 1) % buf.length; count++; notempty.signal(); finally { lock.unlock(); public Object fetch() throws InterruptedException { lock.lock(); try { while (count == 0) notempty.await(); Object x = buf[front]; front = (front + 1) % buf.length; count--; notfull.signal(); return x; finally { lock.unlock(); INF3140 / 2007-10-17 Samtidig programming i Java 23 Samtidig programmering er vanskelig... Metoden stop() i Thread-klassen: Stopper vilkårlige tråder Stopping kan bryte invarianter i låste objekter Ble etterhvert erklært ubrukbar (eng. deprecated) Det var lenge alvorlige feil i Javas minnemodell: Double-checked locking et vanlig idiom i samtidig programmering var ugyldig i Java Feil og uklarheter i definisjonen av lavnivåoppførselen til Java-tråder Rettet i Java 5.0 og Java Language Specification, 3. utgave. INF3140 / 2007-10-17 Samtidig programming i Java 24
Språket JR Språket JR er Java utvidet med språklig støtte for både samtidig og distribuert programmering. JR representerer en alternativ tilnærming, inspirert av Andrews språk SR som brukes i læreboka. En JR-implementasjon er fritt tilgjengelig for ulike operativsystemer. The JR Concurrent Programming Language, http://www.cs.ucdavis.edu/~olsson/research/jr/ INF3140 / 2007-10-17 Samtidig programming i Java 25