Concurrency in Java met threads. Java Threads. Voorbeelden concurrency in applicaties. Waarom concurrency in Java?



Vergelijkbare documenten
Centrale begrippen hoofdstuk 3. Waarom multiprogramming? Vandaag. processen proces state: running, ready, blocked,... Vragen??

Programmeren in Java 3

Gelijktijdigheid: Wederzijdse Uitsluiting & Synchronisatie Concurrency: Mutual Exclusion & Synchonization (5e ed: , Appendix A.

Hoofdstuk 3: Processen: Beschrijving en Besturing. Wat is een proces? Waarom processen? Wat moet het OS ervoor doen? Is het OS zelf een proces?

Tentamen Object Georiënteerd Programmeren TI januari 2013, Afdeling SCT, Faculteit EWI, TU Delft

Zelftest Programmeren in Java

IMP Uitwerking week 13

Modelleren en Programmeren

ICT Infrastructuren: Processen en Threads. 18 november 2013 David N. Jansen

DIAGNOSTISCHE TOETS Softwaresystemen UITWERKING

Modelleren en Programmeren

Belangrijkste ideeën/concepten uit OS, incl. proces

Universiteit van Amsterdam FNWI. Voorbeeld van tussentoets Inleiding programmeren

TECHNISCHE UNIVERSITEIT EINDHOVEN Faculteit Wiskunde en Informatica

Multi-core systemen. door Alexander Melchior

Modelleren en Programmeren

Tentamen Imperatief en Object-georiënteerd programmeren in Java voor CKI

Kleine cursus PHP5. Auteur: Raymond Moesker

Conclusies over semaforen

Vakgroep CW KAHO Sint-Lieven

Programmeren in Java les 3

Datastructuren en algoritmen

Tentamen Object Georiënteerd Programmeren TI oktober 2014, Afdeling SCT, Faculteit EWI, TU Delft

Semaforen. Semaforen p. 1/2

Modelleren en Programmeren

Modelleren en Programmeren

Modelleren en Programmeren

Tentamen Imperatief Programmeren


Voorbeeldtentamen Inleiding programmeren (IN1608WI), Oktober 2003, , Technische Universiteit Delft, Faculteit EWI, Afdeling 2.

Ingebouwde klassen & methodes

in Operating System Concepten

Real-Time Software (RTSOF) EVMINX9 Week 2

in Operating System Concepten Doel van een Operating System Interrupts 3-Lagen model spooling (Simultaneous Peripheral Operation On Line)

Real-Time Systems (RTSYST)

Java virtuele machine JVM

Overerving & Polymorfisme

Dergelijke functionaliteit kunnen we zelf ook aan eigen code toevoegen.

Een gelinkte lijst in C#

Informatica. Deel II: les 1. Java versus Python. Jan Lemeire Informatica deel II februari mei Parallel Systems: Introduction

Programmeren in Java 3

Hoofdstuk 1: Inleiding. Hoofdstuk 2: Klassen en objecten Datahiding: afschermen van implementatiedetails. Naar de buitenwereld toe enkel interfaces.

slides3.pdf April 15,

Uitwerkingen Tweede deeltentamen Imperatief programmeren Vrijdag 15 oktober 2010, uur

Programmeren in Java 3

Stacks and queues. Hoofdstuk 6

Datastructuren: stapels, rijen en binaire bomen

Tim Mallezie Architectuur van besturingssystemen: Vraag A2.

Stacks and queues. Introductie 45. Leerkern 45. Terugkoppeling 49. Uitwerking van de opgaven 49

Deeltentamen Grammatica s en ontleden 22 december 2005

Uitwerking Tweede deeltentamen Imperatief programmeren - versie 1 Vrijdag 21 oktober 2016, uur

Aanvullende toets Gameprogrammeren (INFOB1GP) Woensdag 24 december 2014, uur

Toets In2305-ii Embedded Programming Dinsdag 28 November 2006, 15:45-16:30

IN1805 I - Operating System Concepten

Dynamisch Programmeren. Het Rugzakprobleem

Het begrip 'Proces' Proces-toestand

Lessen Java: Reeks 3. David Blinder Jan G. Cornelis

DOMjudge teamhandleiding

Waarmaken van Leibniz s droom

Tentamen Objectgeorienteerd Programmeren TI februari Afdeling ST Faculteit EWI TU Delft

Omschrijf bij ieder onderdeel van de methode de betekenis ervan. Java kent twee groepen van klassen die een GUI kunnen maken: awt en swing.

Modelleren en Programmeren

Tijdens het tentamen mag het cursusmateriaal niet geraadpleegd worden.

A2: Bedrijfssystemen. A2 en PC. Boek. Voorkennis

Teamhandleiding DOMjudge (versie 2.2.0muKP) 31 mei 2008

Real-Time Systems (RTSYST)

Informatica. Deel II: les 1. Java versus Python. Jan Lemeire Informatica deel II februari mei Parallel Systems: Introduction

Datatypes Een datatype is de sort van van een waarde van een variabele, veel gebruikte datatypes zijn: String, int, Bool, char en double.

Voorkennis: C, basiskennis microprocessoren (bij voorkeur ARM7 processor)

Objectgeorïenteerd werken is gebaseerd op de objecten die door het systeem gemanipuleerd worden.

Computerarchitectuur en netwerken. Memory management Assembler programmering

recursie Hoofdstuk 5 Studeeraanwijzingen De studielast van deze leereenheid bedraagt circa 6 uur. Terminologie

IN1805 I Operating System Concepten

Computerarchitectuur en netwerken Toets 1 4 okt

4EE11 Project Programmeren voor W. College 3, , Blok D Tom Verhoeff, Software Engineering & Technology, TU/e

INFITT01 - Internettechnologie WEEK 8

Modeleren. Modelleren. Together UML. Waarvan maken we een model? overzicht les 14 t/m 18. ControlCenter 6.2

Deel 1 : Gesloten Boek

ICT Infrastructuren: Concurrency en Deadlock. 25 november 2013 David N. Jansen

NAAM: Programmeren 1 Examen 29/08/2012

Scala. Korte introductie. Sylvia Stuurman

Tentamen in2705 Software Engineering

Examen Programmeren 2e Bachelor Elektrotechniek en Computerwetenschappen Faculteit Ingenieurswetenschappen Academiejaar juni, 2010

HOGESCHOOL VAN AMSTERDAM Informatica Opleiding. CPP 1 van 10

Uitwerking Tentamen Modelleren en Programmeren - versie 1 Woensdag 1 februari 2017, uur

APPLICATIEBOUW 3E COLLEGE: OBJECT GEORIËNTEERD PROGRAMMEREN, METHODEN, PARAMETERS, SCOPE VAN VARIABELEN. Onderdeel van SmartProducts

Recursion. Introductie 37. Leerkern 37. Terugkoppeling 40. Uitwerking van de opgaven 40

is eigenlijk overbodig!

Ontwerp van Informatiesystemen

Inhoud. Eindtoets. Introductie 2. Opgaven 3. Terugkoppeling 12

NHibernate als ORM oplossing

Klassen & objecten, overerving, abstracte klassen, debuggen, interfaces, formulieren, polymorfie, statische methoden, event-handlers

Tentamen Objectgeorienteerd Programmeren IN1205 Voorbeeld

Datastructuren: stapels, rijen en binaire bomen

Game of Life in Java

Zelftest Inleiding Programmeren

Beginselen van programmeren Practicum 1 (Doolhof) : Oplossing

Klassen. Hoofdstuk Klassen

Opgaven Registers Concurrency, 29 nov 2018, Werkgroep.

Transcriptie:

Java Threads Concurrency in Java met threads Wat zijn threads? Hoe werken threads? Hoe werk je met threads in Java? Scheduling Synchronisatie In Java programma s is concurrency (aka parallellisme) mogelijk. Concurrency in Java gaat dmv threads. Definitie: een thread is een executie van een sequentiëel programma In een multi-threaded Java programma zijn er meerdere threads, die tegelijk aan verschillende taken werken (die parallel uitgevoerd worden) lock-synchronisatie (met objecten als locks) conditie-synchronisatie 1 2 Voorbeelden concurrency in applicaties Waarom concurrency in Java? desktop, waar gebruiker tegelijkertijd meerdere applicaties draait webbrowser, die animatie met geluid laat zien & horen, terwijl de gebruiker kan scrollen, forms kan invullen, links kan saven, etc. vrijwel elke applicatie met een gecompliceerde gui sterker nog: vrijwel elke interactieve applicatie Vrijwel elke realistische applicatie is multi-threaded. Aangezien zoveel applicaties multi-threaded zijn: Java concurrency is essentiële bijdrage aan portabiliteit (de write once, run anywhere filosofie) Concurrency in bijv. C programma s moet mbv. mechanismen van het onderliggende operating system (eg. POSIX threads op UNIX, Win32API op Windows). Maar: concurrency in Java wel degelijk beinvloed door onderliggende OS. Er zijn meer talen met ingebouwde concurrency, bijv. Concurrent Pascal, Ada, C#, Eerste OO taal (Simula) had al concurrency! geen toeval. Dit is 3 4

Single-threaded Java Executie van o.m(20) is één thread: m(int i){ byte x; a.p(i+1); b.q(i*20); (Terzijde: recursie m(int i){ if (i>0) m(i-1); leidt tot meerdere invocaties van dezelfde methode, elke met eigen entry op de stack. ) Er is een stack voor administratie van uitstaande method calls, bijv. return adres, waarden van parameters en andere lokale variabelen (eg. i en x), waarde van this oftewel het object waarvan de methode aangeroepen is. Wortel van de stack: aanroep van main(..) 5 6 Multi-threaded Java - het idee Echte vs Nep Concurrency Het idee: parallelle executie van method calls. Bijv. executie van o.m(20) gaat verder terwijl a.p(21) en b.q(60) worden uitgevoerd m(int i){ "nieuwe thread" a.p(i+1); "nieuwe thread" b.q(i*20); Executie van o.m(20) resulteert dus in 3 threads. Elke thread heeft zijn eigen stack, maar de threads delen alle objecten (die op de heap staan) Dit kan alleen als er niet teveel afhankelijkheden tussen a.p, b.q en o.m zijn; bijv, de methodes a.p, b.q moeten tenminste void zijn. Parallele executie van threads kan nep of echte concurrency zijn: true concurrency: threads draaien op verschillende processoren op multi-processor machine interleaving aka nep-concurrency: threads draaien om de beurt op dezelfde processor Altijd als #threads > #processoren VM en/of onderliggende OS bepalen welke thread aan de beurt komt: scheduling Nep-concurrency is verreweg het gebruikelijkst (en dit zal voorlopig wel zo blijven, zolang Moore s law prima op blijft gaan voor uni-processor machines). 7 8

Elke thread heeft zijn eigen stack, maar alle threads werken met dezelfde heap. Dwz er is interactie tussen threads door competitie voor processor(en) toegang tot shared objecten Dit tweede punt is de wortel van al het kwaad. Meer hierover later. Waarom zou je meerdere threads willen? Functionaliteit. Handig (of essentieel) voor de applicatie, voor concurrente taken (bijv. animatie terwijl gebruiker kan scrollen), of periodieke taken (bijv. backup, of verversen grafische display bij animaties) zijn. Performance: snellere executie door interleaven van trage I/O met rekenwerk (Maar: ook overhead voor context switching en synchronisatie) (Zelden of nooit: snellere executie dankzij benutten multiprocessor) betere responsetijd: bijv. door aparte thread voor gebruikersinvoer, of timeout voor onbetrouwbare I/O. 9 10 Snellere executie dankzij threads Threads, en programma s in het algemeen, zijn vaak bezig met wachten op I/O operaties! Speedup door interleaven van trage I/O operaties met rekenwerk. Bijvoorbeeld: stel we hebben threads die het volgende doen while(true) { // haal webpagina op: contents = new URL(); // analyseer contents waar ophalen webpagina 100 msec duurt, en analyse ervan 10msec. Welke speedup halen we met 2 ipv 1 thread? Dit is vrijwel altijd een belangrijkere tijdwinst dan benutten van eventuele multi-processor capaciteit. Een thread is een object. Threads in Java Zo n object wordt gecreerd met new en een constructoraanroep van een klasse. Deze klasse moet de klasse Thread extenden, of de interface Runnable implementeren. Een thread-object heeft velden en methodes, kan als parameter meegegeven worden, kan in een array gestopt worden, etc. Eeen thread-object heeft een speciale void methode run(), die de thread iets te doen geeft (vgl. main, maar dan zonder argumenten) De run methode van een thread t laten we uitvoeren met een aanroep van t.start(). 11 12

Threads in Java - subclassing Thread Threads in Java - subclassing Thread public class PrintThread extends Thread{ private String str; public PrintThread(String str){ this.str = str; public void run(){ while(true) System.out.print(str); System.out Heap str = A ta str = B tb public static void main (String[] args){ PrintThread ta = new PrintThread("A"); PrintThread tb = new PrintThread("B"); ta.start(); tb.start(); main s stack System.out.println() run ta s stack run tb s stack 13 14 Threads in Java - subclassing Thread Threads en Processen Je kunt start() ook in de constructor aanroepen. (Typisch als laatste statement. Waarom?) public class PrintThread extends Thread{ private String str; public PrintThread(String str){ this.str = str; this.start(); public void run(){ while(true) System.out.print(str); Een executie van een Java programma wordt een proces genoemd. Een proces bestaat uit één of meer threads. Een thread eindigt (sterft) als het klaar is met z n run methode. Een single-threaded Java programma eindigt als het klaar is met z n main methode. Een multi-threaded Java programma eindigt als al z n threads klaar zijn. (excl. threads die als deamon threads aangewezen zijn, met setdaemon(true)), of als één van z n threads System.exit(int) doet. public static void main (String[] args){ PrintThread ta = new PrintThread("A"); PrintThread tb = new PrintThread("B"); 15 16

Implementing de interface Runnable Runnable implementen ipv Thread extenden: public class PrintRun implements Runnable{ private String str; public PrintRun(String str){ this.str = str; public void run(){ while(true) System.out.print(str); public static void main (String[] args){ PrintRun runa = new PrintRun("A"); PrintRun runb = new PrintRun("B"); Thread ta = new Thread(runa); ta.start(); Thread tb = new Thread(runb); ta.start(); Implementing de interface Runnable Implementen van de interface Runnable is iets ingewikkelder dan extenden van de klasse Thread: 2 new operaties nodig voor het starten van een thread: één om een Runnable object te maken, een tweede om hier een Thread van te maken. De enige reden om Runnable te implementeren ipv Thread te extenden: als je al een andere klasse wilt extenden, bijv. public MyThread extends SomeSuperClass implements Runnable{. Als Java multiple inheritance had, was Runnable niet nodig. NB twee new s nodig voor elke thread. 17 18 Scheduling Scheduling: yield Er is veel vrijheid met scheduling van threads! Er wordt een willekeurige thread gekozen die runnable is; als deze thread niet verder wil of kan, wordt er een andere runnable thread gekozen, én op een willekeurig moment kan de thread onderbroken worden om een andere runnable thread de beurt te geven. Dit heet preemption. Maar: er is geen garantie dat dit ooit gebeurt. Bijv, geen garantie dat thread tb ooit aan de beurt kunnen komen, zolang als thread ta verder wil. Scheduling van de threads bepaald door de VM implementatie en het onderliggende OS. Sommige OS en gaan eerder tot preemption over dan andere. Met yield geeft een runnable thread voorrang aan andere runnable threads. Bijv. met public class PrintThread extends Thread{ public void run(){ while(true) { System.out.println(str); yield(); // geef andere thread de beurt zullen beide threads, ta en tb, aan de beurt komen, en de verdeling van tijd eerlijker zijn. 19 20

Scheduling: priorities Wanneer is thread niet runnable? Je kunt threads verschillende prioriteiten geven public static void main (String[] args){ PrintThread ta = new PrintThread("A"); PrintThread tb = new PrintThread("B"); ta.setpriority(thread.max_priority); tb.setpriority(ta.getpriority()-10); ta.start(); tb.start(); Maar verwacht hier niet te veel van! Het is maar de vraag hoeveel effect dit heeft op de scheduling. Een thread kan in 4 toestanden verkeren new thread kan enkel gestart worden runnable (aka ready) de thread kan iets doen not runnable (aka blocked) de thread kan (tijdelijk) niets doen, want hij slaapt wacht tot een andere thread klaar is wacht op uitvoering van I/O operatie wacht op een lock dead 21 22 sleep Met sleep laat je een thread slapen; wakker worden gaat gepaard met een InterruptedException. public class Clock extends Thread{ new Thread toestanden private long time; public Clock(){ this.start(); public void run(){ while(true) { System.out.println(time); try { sleep(100); // 100 millisec catch(interruptedexception e){; //negeer time += period; Of gebruik java.util.timer(task) runnable dead start() klaar sleep, wait,... ontwaak, ontblok,... Ontwaken, ontblokken,... gaat met een InterruptedException. blocked 23 24

join Met join laat je threads op elkaar wachten; join Typisch gebruik je join voor iets als // start nieuwe thread t MyThread t = new MyThread(.); t.start(); // ga iets anders doen // wacht tot thread t helemaal klaar is try { t.join(); catch (InterruptedException e) { Net als bij sleep, gaat wakker worden uit join gepaard met een InterruptedException. byte[] buffer = new b[1024] // start nieuwe thread t, om buffer te vullen MyThread t = new BufferVuller(b); t.start(); // ga iets anders doen // wacht tot thread bv helemaal klaar is try { t.join(); catch (InterruptedException e) { // ga nu data in buffer verwerken 25 26 Synchronisatie Synchronisatie: voorbeeld public class PrintThread extends Thread{ join is een voorbeeld van synchronisatie tussen threads. De interactie van threads door access aan shared objects vereist synchronisatie, om ongewenste interacties uit te sluiten cq. om gewenste interacties te garanderen. Het regelen van de synchronisatie is het probleem bij concurrency: Er kan van alles missgaan als verschillende threads tegelijkertijd aan dezelfde objecten, datastructuren, of I/O devices zitten. Dit voorkomen we door synchronisatie tussen de threads toe te voegen Maar door het toevoegen van synchronisatie kan er ook weer van alles misgaan... private String str; public PrintThread(String str){ this.str = str; public void run(){ while(true) System.out.println(str); public static void main (String[] args){ PrintThread ta = new PrintThread("abcd"); PrintThread tb = new PrintThread("1234"); ta.start(); tb.start(); Kan dit ooit de uitvoer.ab123cd4 produceren? 27 28

Synchronisatie: voorbeeld Synchronisatie: voorbeeld Als System.out.println geimplementeerd is als public void println(string str){ for (int i=0; i<str.length();i++) System.out.print(str.charAt(i)); System.out.println(); dan kan de uitvoer.ab123cd4 voorkomen namelijk als thread ta halverwege zijn System.out.println(str) ge-preempt wordt. Als System.out.println geimplementeerd is als public void synchronized println(string str){ for (int i=0; i<str.length();i++) System.out.print(str.charAt(i)); System.out.println(); dan kan de uitvoer.ab123cd4 niet voorkomen. Een synchronized methode m van een object o kan niet onderbroken worden door een andere synchronized methode op hetzelfde object. Dit wordt ook wel mutual exclusion genoemd. 29 30 Synchronized methodes Synchronized methodes Ieder Java object heeft een synchronisatie-lock. Methodes kunnen als synchronized gedeclareerd worden. Een thread kan alleen een synchronized methode uitvoeren als het de lock voor het betreffende object heeft. Zoniet, dan blockt de thread tot dat lock vrijkomt. Dwz voor synchronized method calls op hetzelfde object hebben we mutual exclusion. Een thread houdt het lock als in een synchronized methode willekeurige andere methodes worden aangeroepen. Ihb. een synchronized methode kan dus andere synchronized methodes op hetzelfde object aanroepen. Elke thread houdt een verzameling locks, die je kunt bepalen gegeven de stack van die thread. (Hoe?) Hierdoor wordt deadlock mogelijk! (Waarom?) NB andere threads kunnen wel niet-synchronized methodes uitvoeren op o terwijl een thread bezig is met een synchronized methode op o. 31 32

Synchronized statische methodes Terminologie Synchronized voor statische methodes werkt ongeveer hetzelfde als voor instance methodes: Net als elk object, synchronisatie lock. heeft ook elke klasse een Een thread kan alleen een synchronized static methode uitvoeren als het het de lock voor betreffende klasse heeft. Dus voor de synchronized statische methodes van een klasse hebben we mutual exclusion. Nog wat terminologie: Een object met synchronized methodes ook wel een monitor genoemd. Een methode of, algemener, een code fragment waarvoor we mutual exclusion hebben wordt wel een kritische sectie (critical section) genoemd. Als een method call of, algemener, een code fragment nooit onderbroken (pre-empted) kan worden, noemen we het een atomaire operatie. Atomiciteit is een sterkere eigenschap dan mutual exclusion: een synchronized methode kan wel degelijk onderbroken worden, alleen niet door aanroepen van andere synchronized methodes op hetzelfde object. 33 34 Synchronized methodes: voorbeeld Welke methodes moeten hier synchronized zijn? public class Log{ private Vector log; private int size = 0; int getsize() { return size; String getlastentry() { return (String)log.elementAt(size); void addentry(string s){ log.addelement(s); size++; Synchronized methodes: voorbeeld Vaak maar niet altijd moeten set-methodes synchronized zijn, en hoeven get-methodes dat niet te zijn. public class Log{ private Vector log; private int size = 0; int getsize() { return size ; String getlastentry() { return (String)log.elementAt(size); ; void synchronized addentry(string s){ log.addelement(s); size++; Subtiel punt: volgorde van operaties in addentry! 35 36

Gevaren van synchronisatie Alternatieven voor synchronisatie Het gebruik van synchronized is niet zonder gevaren: Deadlock Een programma kan vastlopen doordat threads om elkaar wachten Starvation Een enkele thread kan nooit meer aan de beurt komen omdat de vereiste locks nooit kan bemachtigen. Er zijn meerdere manieren om ongewenste interacties van threads uit te sluiten: 1. Eén manier is het gebruik van synchronized. 2. Een andere, betere, manier is ervoor te zorgen dat threads geen objecten delen, zodat synchronisatie onnodig wordt. Bijv. laat elke PrintThread z n uitvoer naar een ander window sturen, of geef elke thread z n eigen Log. Indien mogelijk, heeft dit de voorkeur. Voorkomen is beter dan genezen! 3. De twee technieken hierboven kunnen niet voldoende zijn: naast het gebruik van synchronized, moeten moeten er dan extra, fijnere synchronisaties toegevoegd worden 37 38 Fijnere synchronisatie Fijnere synchronisatie dan synchronized van methodes is mogelijk door een blok code te synchronizen met de lock van een willekeurig object Bijv. class PrintThread extends Thread{ private String str; public void print(){ synchronized(system.out){ System.out.print("De uitvoer van thread"); System.out.print(this.getName()); System.out.print(" : ") System.out.println(str); voorkomt dat uitvoer van verschillende PrintThread s door elkaar komt. (Mooi is anders: liever laat je alle synchronisatie door het object System.out zelf regelen.) Fijnere synchronisatie Je kunt ook dummy objecten gebruiken als locks. Deze dummy objecten representeren het recht om een bepaalde resource (bijv. een I/O device) te gebruiken. Bijv. final static Object resource1 = new Object(); final static Object resource2 = new Object(); class PrintThread extends Thread{ public void a(){ synchronized(resource1){ synchronized(resource2){ // beide resources geclaimd In een mooi OO ontwerp wil je zulke dummy objecten vermijden, en synchronisatie liever regelen dmv. synchronized methodes op objecten! 39 40

Fijnere synchronisatie : conditie-synchronisatie Soms wil je een thread laten wachten tot een bepaalde conditie geldt, bijvoorbeeld tot een queue niet meer leeg is: public class Queue { Linkedlist q = new LinkedList(); public synchronized? Object pop(){ while (q.size() == 0) { "wacht tot de q niet leeg is"; return q.remove(0); public synchronized? push (Object x){ q.add(x); Conditie-synchronisatie Bij elk object hoort behalve een lock ook een wachtrij met threads. Met o.wait() gaat een thread in de wachtrij van object o staan, en geeft het lock van het object vrij (als de thread dat block had). Met o.notifyall() maak je alle threads in de wachtrij van o wakker. Deze threads gaan verder, maar moeten wel het het lock van o weer krijgen als ze met een synchronized methode verder willen. Wakker worden gaat weer gepaard met een InterruptedException. Hoe doe je dit? Dit is meer dan mutual exclusion, en met een simpele lock lukt t niet. 41 42 Conditie-synchronisatie: wait en notifyall public class Queue { Linkedlist q = new LinkedList(); public synchronized Object pop(){ while (q.size() == 0) { try {this.wait(); catch (InterruptedException e) { return q.remove(0); public synchronized push (Object x){ q.add(x); this.notifyall(); Conditie-synchronisatie: while ipv if! De standaard-manier om op een conditie te synchroniseren is met public synchronized Object pop(){ while ( conditie ) { try {this.wait(); catch (InterruptedException e) { en niet met public synchronized Object pop(){ if ( conditie ) { try {this.wait(); catch (InterruptedException e) { Gebruik van if is meestal incorrect! 43 44

Conditie-synchronisatie: while ipv if! notifyall() vs notify() Waarom gaat dit programma mis? public class QueueFout { Linkedlist q = new LinkedList(); public synchronized Object pop(){ if (q.size() == 0) { try {this.wait(); catch (InterruptedException e) { return q.remove(0); Met o.notifyall() maak je alle threads in de wachtrij van object o wakker. Met o.notify() maak je één thread in de wachtrij van object o wakker. Het gebruik van notify is link omdat het risico van starvation van threads erg is. QueueFout is ook incorrect als je de notifyall door notify vervangt. public synchronized push (Object x){ q.add(x); this.notifyall(); Merk op: programma s met wait en notifyall zijn lastig. 45 46 wait(timeout) Concurrency is moeilijk! Ipv op notify(all) te vertrouwen om threads te wekken, kan wait ook een time-out meekrijgen: public synchronized Object pop(){ while ( conditie ) { try {this.wait( timeoutinmillisecs ); catch (InterruptedException e) { Dit is inefficienter maar robuuster dan op notify(all) te vertrouwen. Concurrency handig/essentiëel, maar moeilijk! Mogelijke bugs door concurrency: rare interacties tussen threads door onvoldoende synchronisatie (aka. data races) deadlock starvation Minimaliseer bij je ontwerp het gebruik van shared objects, en minimaliseer dus de noodzaak voor synchronisatie. Multi-threaded programma s gedragen zich nondeterministisch, want gedrag afhankelijk van allerlei omgevingsfactoren (scheduling, snelheid I/O, user-interacties) Dit non-determinisme maakt debuggen moeilijk! Merk op: bij C(++) zijn single-threaded programma s al non-determistisch, bijv. door pointer-manipulaties en ontbreken default-initialisatie! 47 48

Shared variables Shared variables Interactie tussen threads is niet alleen mogelijk door al dan niet synchronized methodes van shared objecten aan te roepen. Threads kunnen ook direct aan shared velden komen. Normaliter wil je dit zoveel mogelijk vermijden! public class Weird{ int x,y,i,j; class Thread1 extends Thread { public void run(){ x=1; j=y; class Thread2 extends Thread { public void run(){ y=1; i=x; public void main (String[] args){ new Thread1().start(); new Thread2().start(); Kan na afloop i=0 én j=0 zijn? 49 50 Shared variables volatile velden Antwoord: JA! Want compiler kan volgorde van statements veranderen compiler kan waarden in registers opslaan op multiprocessorsysteem kunnen threads tijdelijk een lokale kopie van globale variabelen bijhouden. Deze lokale kopieën kunnen tijdelijk verschillen van de waarden in het globale geheugen; het globale geheugen loopt soms achter, omdat writes gebufferd worden. De modifier volatile voor veld waarschuwt dat verschillende threads er tegelijkertijd aan kunnen zitten: public class Weird{ volatile int x,y; int i,j; class Thread1 extends Thread { public void run(){ x=1; j=y; class Thread2 extends Thread { public void run(){ y=1; i=x; maar vermijden van dit soort shared velden is veel beter! 51 52

Java Memory Model Real-time Java Exacte semantiek van threads, synchronized, volatile etc. is erg ingewikkeld (vooral voor slechtgesynchroniseerde code). Hoofdstuk 17 van de Java Language Spec (H 8 van de Java Virtual Machine Spec) is onbegrijpelijk en incorrect. Geen enkele implementatie houdt zich aan deze spec. Actief onderwerp van onderzoek & discussie over de afgelopen jaren. Factoren van invloed: niet alleen implementatie van de VM en onderliggende hardware, maar ook compileroptimalisaties. Java biedt weliswaar concurrency, maar is ongeschikt voor echte real-time applicaties: scheduling erg vrijblijvend wachtende threads niet noodzakelijk FIFO hierdoor: timing erg onvoorspelbaar Er is een standaard voor real-time Java ontwikkeld, JSR-1, zie www.rtj.org. Verbeterde definitie, JSR-133, Java Memory Model and Thread Specification Revision, goedgekeurd 15 september 2003. Trend: hogere programmeertalen (met bijv. garbage collection) in steeds meer devices & toepassingen. 53 54