synchronized - Universitetet i Oslo

INF1010
Tråder2
31.mars2016
SteinGjessing
Ins$tu'forinforma$kk
UniversitetetiOslo
1
Ins$tu'forinforma$kk
Kommunikasjon mellom tråder:
Felles data
F
K
Felles data (blå felt, F) må vanligvis bare
aksesseres (lese eller skrives i) av en tråd om
gangen. Hvis ikke blir det kluss i dataene.
Et felles objekt kalles en monitor.
Metodene i en monitor har modifikatoren
synchronized
F
K
f.eks.
K: konstante data
(immutable)
putInn
Monitor
er ikke
noe ord
i Java
hentUt
Buffer
Produsenter
Konsumenter
2
Ins$tu'forinforma$kk
BasaleJava-verktøy:
synchronized,wait(),no$fy()ogno$fyAll()
(metodene er definert i class Object)
<Buffer-data>
produsenter
while(){
<lagnoe>;
p.putInn(...);
}
konsumenter
synchronizedvoidputInn(intverdi)
while(){
p.hentUt
<brukde'e>;
}
while(full)wait();
//nåerbufferetikkefult
<legginn>
synchronizedinthentUt
while(){
<lagnoe>;
p.putInn(...);
}
If(empty)returnnull;
else
<tautetelement>
//nåerbufferetikkefult
no3fy();
returnelement;
Enmonitor(etobjekt)
while(){
p.hentUt
<brukde'e>;
}
Passpååunngå:
polling=ak$vven$ng
(prøveomogomigjen
3
(utenåfågjortnoe))Ins$tu'forinforma$kk
To køer i en basal Java monitor:
<Buffer-data>
Enkøavventendetråder
påhelemonitoren
køforåfålåsen($lgang$lmonitoren)
køavventendetråder
synchronizedvoidputInn(intverdi)
while(full)wait();
//nåerbufferetikkefult
<legginn>
synchronizedinthentUt
If(empty)returnnull;
else
<tautetelement>
//nåerbufferetikkefult
no3fy();
returnelement;
Enmonitor(etobjekt)
Enkøavventendetråderpå
”wait”-instruksjoner
(wait-set).
Startesavno$fy()og/eller
no$fyAll()
Leggesdaidenandrekøen
(først?(Nei,ingengaran$))
Derforerdetnødvendigmed
”while…”
4
Ins$tu'forinforma$kk
Javaharénkø
forallewait()-
instruksjonene
påsammeobjekt!
produsenter
while(){
<lagnoe>;
p.putInn(...);
}
<Buffer-data>
ENkøforåfålåsen
ENkøforventendetråder
synchronizedvoidputInn(intverdi)
konsumenter
while(full)wait();
//nåerbufferetikkefult
<legginn>
//nåerbufferetikketomt
no3fy();
synchronizedinthentUt
while(){
<lagnoe>;
p.putInn(...);
}
while(empty)wait();
//nåerbufferetikketomt
<tautetelement>
//nåerbufferetikkefult
no3fy();
returnelement;
Enmonitor(etobjekt)
while(){
p.hentUt
<brukde'e>;
}
while(){
p.hentUt
<brukde'e>;
}
Passpå:Unngåvranglås
5
(deadlock)
Ins$tu'forinforma$kk
<Buffer-data>
Flytt en: notify ();
køforåfålåsen
Flytt alle: notifyAll ();
ventendetråder
intledig=totRes;
//invariant:0<=ledig<=totRes
synchronizedvoidreserver(intantall)
while(ledig<antall)wait();
//nåerdetnokledig
ledig=ledig–antall;
synchronizedvoidfrigi(intantall)
ledig=ledig+antall;
//nåermangeflereledig
no3fyAll();
Enmonitor(etobjekt)foråreservereogfrigi
etantalllikeresurser
Hvis du ikke er HELT sikker på at enhver
ventende tråd kan ordne skikkelig opp
må du si:
notifyAll();
Da blir ALLE ventende tråder flyttet opp
til køen for å få låsen
Men hva med
rettferdighet
(fairness) ??
6
Ins$tu'forinforma$kk
teknikk for å
Eksempelpåparallellisering: (samme
finne sum / gjennomsnitt)
Finnminstetallitabell
HovedprogrammetstarterN
Tråd1finnerminstetalli
tråderogventerpåatdealle
erferdigeførdethenterminste
verdiframonitorenMinstVerdi
dennedelenavtabellen
Tråd2finnerminstetalli
dennedelenavtabellen
synchronizedvoidgiMinsteVerdi(intverdi)
synchronizedinthentMinst
Objektavklassen
MinstVerdi
Trådenes
minste
verdigis$l
monitoren
Trådn(64?)finnerminstetalli
dennedelenavtabellen
BARE FANTASIEN
SETTER GRENSER
7
Ins$tu'forinforma$kk
Amdahlslov
•  Enberegningdeltoppiparallellgårforterejomeruavhengigdeleneer
•  Amdahlslov:
–  Total$dener
•  $deniparallell+
•  $dendettaråkommunisere/synkronisere/gjørefellesoppgaver
–  Tidendettaråsynkronisereerikkeparallelliserbar(hjelperikkemedflere
prosessorer)
–  Mendukanværesmartoglagesynkroniseringensåkortellermellomsåfå
trådersommulig
total tid
Start opp
Synkroniser
Parallelle beregninger
Lag resultat
Parallelle beregninger
8
Ins$tu'forinforma$kk
Amdahlslovog”finnminstetall”
•  Total$d:
–  Tidendettarålageogse'eigang64tråder
*
•  (kandetgjøresiparallell?)(log64=6)
2
–  Tidendettaråfinneetminstetallimindelavtabellen
–  Tilslu'imonitoren:Enogentrådmåtestesi'resultat
•  (Kandetgjøresiparallell?)
total tid
Start opp n tråder
Parallelle beregninger
Synkronisering
Lag endelig resultat
6
* Start med én tråd, vha. doblinger 6 ganger har vi 64 tråder, dvs, 2 = 64, og log2 64 = 6
2, 4, 8, 16, 32, 64
9
Ins$tu'forinforma$kk
Hovedprogrammetversjon1
public class MinstR {
final int maxVerdiInt = Integer.MAX_VALUE;
int [ ] tabell;
MinstMonitorR monitor;
public static void main(String[ ] args) {
new MinstR();
}
public MinstR ( ) {
tabell = new int[640000];
for (int in = 0; in< 640000; in++)
tabell[in] = (int)Math.round(Math.random()* maxVerdiInt);
monitor = new MinstMonitorR();
for (int i = 0; i< 64; i++)
// Lag og start 64 tråder
new MinstTradR(tabell,i*10000,((i+1)*10000)-1,monitor).start();
monitor.vent();
System.out.println("Minste verdi var: " + monitor.hentMinste());
}
}
10
Ins$tu'forinforma$kk
class MinstMonitorR {
private int minstTilNa = Integer.MAX_VALUE;
private int antallFerdigeSubtrader = 0;
synchronized public void vent() {
while (antallFerdigeSubtrader != 64) {
try {wait();
System.out.println(antallFerdigeSubtrader + "
ferdige subtråder ");
}
catch (InterruptedException e) {
System.out.println(" Uventet avbrudd ");
System.exit(1);
}
// antall ferdige subtråder er nå 64
}
}
synchronized public void giMinsteVerdi (int minVerdi) {
antallFerdigeSubtrader ++;
if(minstTilNa > minVerdi) minstTilNa = minVerdi;
if(antallFerdigeSubtrader == 64) notify();
// eller hver gang, (men stort sett unødvendig): notify();
}
synchronized public int hentMinste () {
return minstTilNa;
}
}
11
Ins$tu'forinforma$kk
Trådersomfinnerminstetallidelavtabellen
class MinstTradR extends Thread {
int [ ] tab; int startInd, endInd;
MinstMonitorR mon;
MinstTradR(int [ ] tb, int st, int en, MinstMonitorR m) {
tab = tb; startInd = st; endInd = en;
mon = m;
}
public void run(){
int minVerdi = Integer.MAX_VALUE;
for ( int ind = startInd; ind <= endInd; ind++)
if(tab[ind] < minVerdi) minVerdi = tab[ind];
// denne tråden er ferdig med jobben:
mon.giMinsteVerdi(minVerdi);
} // slutt run
} // slutt MinstTradR
12
Ins$tu'forinforma$kk
Barriereriparallellprogrammering
Barriere, dvs. vent
på at alle er ferdig
Denne tråden kan
fortsetter når alle har
nådd barrieren (join)
Gren opp i parallelle
beregninger (fork)
Alle trådene sier fra når de er ferdige
tid
13
Ins$tu'forinforma$kk
Barriereriparallellprogrammering
import java.util.concurrent.*;
Barrieren
CountDownLatch minBariere =
new CountDownLatch(6)
minBarriere.await();
minBarriere.countDown();
tid
14
Ins$tu'forinforma$kk
Barrierenide'eeksemplet
Start opp
Alle trådene sier fra
når de er ferdige
(har nådd barrieren)
Gren opp i parallelle
beregninger (fork)
Denne tråden kan
fortsetter når alle har
nådd barrieren (join).
Da er det endelige
resultatet klart.
Barieren
Alle trådene avsluttes med
å kalle en metode i en
og samme monitor
tid
Kallet monitor.vent();
i minstR-programmet er en
hjemmelaget barriere
15
Ins$tu'forinforma$kk
Hovedprogrammet,versjonmedbarriere
import java.util.concurrent.*;
public class MinstB {
final int maxVerdiInt = Integer.MAX_VALUE;
int [ ] tabell;
final int antallTrader = 64;
MinstMonitorB monitor = new MinstMonitorB();;
CountDownLatch minBarriere = new CountDownLatch(antallTrader);
public static void main(String[ ] args) {
new MinstB();
}
public MinstB ( ) {
tabell = new int[640000];
for (int in = 0; in< 640000; in++)
tabell[in] = (int)Math.round(Math.random()* maxVerdiInt);
for (int i = 0; i< antallTrader; i++)
// Lag og start antallTrader tråder
new MinstTradB(tabell,i*10000,((i+1)*10000)-1,monitor,minBarriere).start();
//vent på at alle trådene er ferdig:
try {
minBarriere.await(); // vent på at alle ”Minst-trådene” har nådd barrieren
System.out.println("Minste verdi var: " + monitor.hentMinste() );
}
catch (InterruptedException ex){ }
}
}
16
Ins$tu'forinforma$kk
Monitorforresultater
class MinstMonitorB {
private int minstTilNa = Integer.MAX_VALUE;
synchronized public void giMinsteVerdi (int minVerdi) {
if(minstTilNa > minVerdi) minstTilNa = minVerdi;
}
synchronized public int hentMinste () {return minstTilNa;}
}
17
Ins$tu'forinforma$kk
Trådersomfinnerminstetallidelavtabellen
class MinstTradB extends Thread {
int [ ] tab; int startInd, endInd;
CountDownLatch barriere;
MinstMonitorB mon;
MinstTradB(int [ ] tb, int st, int en, MinstMonitorB mon,
CountDownLatch barriere) {
tab = tb; startInd = st; endInd = en;
this.mon = mon; this.barriere = barriere;
}
public void run(){
int minVerdi = Integer.MAX_VALUE;
for ( int ind = startInd; ind <= endInd; ind++)
if(tab[ind] < minVerdi) minVerdi = tab[ind];
// gi minste resultat til monitoren:
mon.giMinsteVerdi(minVerdi);
// signaler at denne tråden er ferdig med jobben:
barriere.countDown(); // sier fra at jeg har nådd barrieren
} // slutt run
} // slutt MinstTradB
18
Ins$tu'forinforma$kk
Mennoengangererdetmuligåsynkroniseremindre
(IKKEvik3giINF1010)
Nyskisse3lmonitorforresultater
class MinstMonitorS {
private int [] deMinste = new int[antallTrader];
private int minstTilNa = Integer.MAX_VALUE;
public void giMinsteVerdi(int tradNr, int minVerdi) {
// Hver tråd har sitt eget element i tabellen
deMinste[tradNr] = minVerdi;
}
synchronized public int hentMinste () { // Bare en tråd kaller
// behøver ikke være synchronized
for (int enVerdi: deMinste) {
if(minstTilNa > enVerdi) minstTilNa = enVerdi;
}
return minstTilNa;
}
}
Hva med Amdahls lov nå?
19
Ins$tu'forinforma$kk
Mennoengangererdetmuligåsynkroniseremindre
(IKKEvik3giINF1010)
Amdahlslovmednyskisse3lmonitorforresultater
•  Total$d:
–  Tidendettarålageogse'eigang64tråder
–  Tidendettaråfinneetminstetallimindelavtabellen
–  Tilslu'imonitoren:
•  Iparallell(blå'påfiguren):Enogentrådleggerinnsi'resultat
•  Tilslu'finnerhovedprogrammetdenminsteverdien
total tid
Start opp n tråder
Parallelle beregninger
Lag endelig resultat
Barriere
*
Sammenlign med lysark 9
Ins$tu'forinforma$kk
Maskinarkitektur,
f.eks.4prosessorer(repe$sjon)
prosessor
registre
cache 1
cache 2
prosessor
registre
cache 1
Minne
(RAM)
prosessor
registre
cache 1
cache 2
prosessor
registre
cache 1
21
Ins$tu'forinforma$kk
Javasminnemodell(memorymodel)
•  Enminnemodeldefineresvedåsepålovligerekkefølgerav
leseogskriveoperasjonerpåvariable.
•  Innadientrådskjer$ngirekkefølgengi'avprogrammet,
bortse'fraatuavhengigeoperasjonerkan”re-ordnes”(for
op$malisering).
•  Mellomtråderskjer$ngikkeirekkefølgeunnta'
–  Vedbrukavvola$levariable
–  Alleoperasjonerføren”unlock”skjerirekkefølgeføralleoperasjoner
e'erene'erfølgende”lock”pådensammemonitoren.
22
Ins$tu'forinforma$kk
Vola$levariable
•  envariabelsomerdeklarertvola$lecachesikke
(ogoppbevaresikkeiregistre(lengerennheltnødvending)).
–  Envola$lvariableskriveshelt$lbakeiprimærlageretførneste
instruksjonuqøres.
•  Rekkefølgenavskriv/les-operasjonerpåvola$levariableog
låsing/opplåsingavlåserdefinererensekvensieltkonsistent
rekkefølgeavalleoperasjonersomerendelavseman$kken
$lprogrammeringsspråketJava.
23
Ins$tu'forinforma$kk
Synkroniseringogfellesvariable
•  Synchroniza$onensuresthatmemorywritesbyathread
beforeorduringasynchronizedblockaremadevisibleina
predictablemannertootherthreadswhichsynchronizeon
thesamemonitor.Arerweexitasynchronizedblock,we
releasethemonitor,whichhastheeffectofflushingthe
cachetomainmemory,sothatwritesmadebythisthread
canbevisibletootherthreads.Beforewecanentera
synchronizedblock,weacquirethemonitor,whichhasthe
effectofinvalida$ngthelocalprocessorcachesothat
variableswillbereloadedfrommainmemory.Wewillthen
beabletoseeallofthewritesmadevisiblebytheprevious
release.
From: JSR 133 (Java Memory Model) FAQ
Jeremy Manson and Brian Goetz, February 2004
24
Ins$tu'forinforma$kk
Synkroniseringogfellesvariable
•  Thismeansthatanymemoryopera$onswhichwerevisibleto
athreadbeforeexi$ngasynchronizedblockarevisibletoany
threadareritentersasynchronizedblockprotectedbythe
samemonitor,sinceallthememoryopera$onshappen
beforetherelease,andthereleasehappensbeforethe
acquire.
From: JSR 133 (Java Memory Model) FAQ
Jeremy Manson and Brian Goetz, February 2004
25
Ins$tu'forinforma$kk
OgfradefinisjonenavJava:Versjon8kap.17.7:
17.7Non-atomicTreatmentofdoubleandlong
For the purposes of the Java programming language memory
model, a single write to a non-volatile long or double value is
treated as two separate writes: one to each 32-bit half. This
can result in a situation where a thread sees the first 32 bits of
a 64-bit value from one write, and the second 32 bits from
another write.
Writes and reads of volatile long and double values are always
atomic. Writes to and reads of references are always atomic,
regardless of whether they are implemented as 32-bit or 64-bit
values.
26
Ins$tu'forinforma$kk
MerfradefinisjonenavJava:Versjon8,kap17.3:
Forexample,inthefollowing(broken)codefragment,assumethat
this.doneisanon-vola$lebooleanfield:
while(!this.done)
Thread.sleep(1000);
Thecompilerisfreetoreadthefieldthis.donejustonce,andreuse
thecachedvalueineachexecu$onoftheloop.Thiswouldmeanthat
theloopwouldneverterminate,evenifanotherthreadchangedthe
valueofthis.done.
27
Ins$tu'forinforma$kk
Fordelenmedkonstanter(immutableobjects)
•  Konstanterkandetaldriskrives$l
•  Minnemodellenforkonstanterblirderforveldigenkel.
•  Prøvåhamestmuligkonstanternårdataskaldelesmellom
tråder.
•  Eksempel:Geografiskedata,observasjoner
•  Hvisduønskerågjøreenforandring:
–  Kastdetgamleobjektetoglagetny'.
28
Ins$tu'forinforma$kk
Vi tar opp tråden (J) fra sidene lenger foran i forbindelse med at tråder skal vente i monitorer:
Vanligviserdetflereforskjelligebe#ngelser
(condi3ons)viventerpåienmonitor:
venterpååkomme
innførstegang
<Buffer-data>
synchronizedvoidsetInn(intverdi)
If(antall==1000)vent-$l:Ikke-Full();
antall++;
:
no$fy:Ikke-Tom();
Men innebygget i java er det
bare en kø.
Hva gjør vi?
synchronizedinttaUt()
If(antall==0)vent-$l:Ikke-Tom();
antall--;
:
no$fy:Ikke-Full();
Enmonitor(etobjekt)
29
Ins$tu'forinforma$kk
Biblioteketjava.concurrent.SlikdetbrukesiHorstmann(læreboka):
Flerekøeravventendetråder
Locklaas=newReentrantLock();
Condi3onikkeFull=laas.newCondi$on();
Condi3onikkeTom=laas.newCondi$on();
voidse'Inn(intverdi)
laas.lock();
try{
while(full)ikkeFull.await();
//nåerdethelstsikkertikkefult
:
//deterlagtinnnoe,sådeter
//heltsikkertikketomt:
ikkeTom.signal();
}finally{
laas.unlock()
}
inttaUt()
Enkøforselvelåsen
ogenkøforhver
condi$on-variabel
(importjava.concurrent.locks.*)
Metodene er ikke lenger ”synchronized”
og vi må låse (og låse opp !)
monitoren selv :-(
30
Ins$tu'forinforma$kk
Java´sAPI
(java.concurrent)
InterfaceCondi3on
•  voidawait()
Causesthecurrentthreadtowait
un$litissignalled(orinterrupted)
•  voidsignal()
Wakesuponewai$ngthread.
•  ClassReentrantLockerenlåsogenfabrikksomlagerobjekter
somimplementerergrensesni'etCondi$on(pådennelåsen)
31
Ins$tu'forinforma$kk
Locklaas=newReentrantLock();
Condi3onikkeFull=laas.newCondi$on();
Condi3onikkeTom=laas.newCondi$on();
voidse'Inn(intverdi)throwsInterrupedExcep$on
laas.lock();
try{
while(full)ikkeFull.await();//OKmedif?
//nåerdetheltsikkertikkefult
:
//deterlagtinnnoe,sådeterheltsikkertikketomt:
ikkeTom.signal();
}finally{
laas.unlock();
}
inttaUt()throwsInterrupedExcep$on
laas.lock();
try{
while(tom)ikkeTom.await();//OKmedif?
//nåerdetheltsikkertikketomt;
:
//deterdetta'utnoe,sådeterheltsikkertikkefult:
ikkeFull.signal();
}finally{
laas.unlock();
}
Nå har vi en kø
per betingelse
som skal
oppfylles
(og vi kan til og med
få rettferdighet!)
Bra!
32
Ins$tu'forinforma$kk
Leggmerke$lbrukenavfinally
voidputInn(intverdi)throwsInterrupedExcep$on{
laas.lock();
try{
:
:
}finally{
laas.unlock();
}
}
Dablirlaas.unlock()all3duqørt!!
33
Ins$tu'forinforma$kk
Prosesservs.tråder
P1
P2
Pn
Operativsystemet
(styrer alt og sørger for
kommunikasjon mellom prosesser)
Datamaskin 1
P1
P2
Pm
Operativsystemet
(styrer alt og sørger for
kommunikasjon mellom prosesser)
Datamaskin 2
34
Ins$tu'forinforma$kk
Kommunikasjon mellom prosesser
Samarbeidende prosesser sender meldinger til hverandre (brune piler, mest vanlig)
eller leser og skriver i felles lager (blå piler).
Prosess 2
MPI: Message Passing Interface
Prosess 1
Prosess 3
Meldingsutveksling
Prosess 1
Prosess 2
Felles data
Prosess 3
I Java: Et objekt
35
Ins$tu'forinforma$kk
Parallellitetmellomprosesser:
Eks:CORBA,MPI(ikkepensumiINF1010)
•  CORBA(CommonObjectRequestBrokerArchitecture)er(var)en
språkuavhengigmåteådefinerekommunikasjonmellomprosesservha.
{ern-objekter
EtIDL(InterfaceDefini$onLanguage)definerergrensesni'et$lobjektene(på
sammemåtesomInterfaceiJava)
•  EnIDL-kompilatoroverse'er$ldi'valgtespråk(Java,C++,C#,Smalltalk,…)
•  De'egjøratprosesserskrevetiforskjelligeobjektorientertespråkogsom
kjørerpåforskjellige(ellersamme)maskinkankommunisere.
•  Språksomikkeerobjektorienterte:
P2
Sendeogmo'ameldinger
(MPI–Message-PassingInterface)
P3
P1
Send en melding
36
Motta en melding
Ins$tu'forinforma$kk
KommunikasjonmellomJava-prosesser:
RMI:RemoteMethodInvoca$on
(Norsk:Fjern-metode-kall)
Dette ønsker vi å oppnå:
Et Java program
(på en maskin)
Et annet Java
program
(på en annen
maskin)
InterfaceNavn fjernPeker = …….;
...
fjernPeker.metodeNavn( );
...
metodeNavn
objekt av en klasse som
implementerer InterfaceNavn
Alternativt navn: RPC: Remote Procedure Call
37
Ins$tu'forinforma$kk
RMI: Implementasjon
(bak kulissene)
En maskin
fjernPeker
en proxy for
fjern-objektet
Ikke pensum i INF1010
En annen
maskin
et skjelett
formidler
kall og retur
metodeNavn
tjenerMetode
...
fjernPeker.metodeNavn( );
...
en stub for
fjern-metoden
Odene proxy og stub brukes
ikke alltid like konsistent
fjern-objekt
(kan også brukes
av Javaprogrammet
på denne maskinen)
Parametere (og returverdi)
pakkes ned og sendes mellom
de to maskinen (marshaling)
38
Ins$tu'forinforma$kk
RMI:Hvordankallemetoderi
(Java-)objekterpåandremaskiner
Ikke pensum i INF1010
klient
tjener
Register/
navnetjener
3. oppgi navn på objekt
4. og få tilbake peker
2.
5. bruk peker til
fjernmetode-kall
(Remote Method
Invocation):
1. registrer (bind)
med navn
på objektet
objekt
fjernPeker.metodeNavn( );
Og det eneste som er kjent på begge maskinene
er Interfacet til objektet og navnet
39
Ins$tu'forinforma$kk