Minne maskinvarearkitektur
Moderne minnemaskinvarearkitektur skiller seg fra Javas interne minnemodell. Derfor må du forstå maskinvarearkitekturen for å vite hvordan Java-modellen fungerer med den. Denne delen beskriver den generelle minnemaskinvarearkitekturen, og den neste delen beskriver hvordan Java fungerer med den.
Her er et forenklet diagram over maskinvarearkitekturen til en moderne datamaskin:
I den moderne verden har en datamaskin 2 eller flere prosessorer, og dette er allerede normen. Noen av disse prosessorene kan også ha flere kjerner. På slike datamaskiner er det mulig å kjøre flere tråder samtidig. Hver prosessorkjerne er i stand til å kjøre én tråd til enhver tid. Dette betyr at alle Java-applikasjoner på forhånd er flertrådede, og i programmet ditt kan én tråd per prosessorkjerne kjøre om gangen.
Prosessorkjernen inneholder et sett med registre som ligger i minnet (inne i kjernen). Den utfører operasjoner på registerdata mye raskere enn på data som ligger i datamaskinens hovedminne (RAM). Dette er fordi prosessoren kan få tilgang til disse registrene mye raskere.
Hver CPU kan også ha sitt eget cachelag. De fleste moderne prosessorer har det. Prosessoren kan få tilgang til cachen mye raskere enn hovedminnet, men ikke så raskt som de interne registrene. Verdien av hurtigbufferens tilgangshastighet er omtrentlig mellom tilgangshastighetene til hovedminnet og interne registre.
Dessuten har prosessorer et sted å ha en multi-level cache. Men dette er ikke så viktig å vite for å forstå hvordan Java-minnemodellen samhandler med maskinvareminne. Det er viktig å vite at prosessorer kan ha et visst nivå av cache.
Enhver datamaskin inneholder også RAM (hovedminneområde) på samme måte. Alle kjerner har tilgang til hovedminnet. Hovedminneområdet er vanligvis mye større enn hurtigbufferminnet til prosessorkjernene.
I det øyeblikket prosessoren trenger tilgang til hovedminnet, leser den en del av det inn i bufferminnet. Den kan også lese noen data fra cachen inn i sine interne registre og deretter utføre operasjoner på dem. Når CPU-en trenger å skrive resultatet tilbake til hovedminnet, vil den tømme dataene fra det interne registeret til hurtigbufferen, og på et tidspunkt til hovedminnet.
Data som er lagret i hurtigbufferen tømmes normalt tilbake til hovedminnet når prosessoren trenger å lagre noe annet i hurtigbufferen. Cachen har muligheten til å tømme minnet og skrive data samtidig. Prosessoren trenger ikke å lese eller skrive hele hurtigbufferen hver gang under en oppdatering. Vanligvis oppdateres cachen i små minneblokker, de kalles "cache-linje". Én eller flere "cache-linjer" kan leses inn i cache-minnet, og én eller flere cache-linjer kan tømmes tilbake til hovedminnet.
Kombinerer Java-minnemodell og minnemaskinvarearkitektur
Som allerede nevnt, er Java-minnemodellen og minnemaskinvarearkitekturen forskjellige. Maskinvarearkitekturen skiller ikke mellom trådstabler og hauger. På maskinvare ligger trådstabelen og HEAP (heap) i hovedminnet.
Deler av stabler og trådhauger kan noen ganger være tilstede i cacher og interne registre til CPU. Dette er vist i diagrammet:
Når objekter og variabler kan lagres i forskjellige områder av datamaskinens minne, kan det oppstå visse problemer. Her er de to viktigste:
- Synlighet av endringene som tråden har gjort i delte variabler.
- Løpstilstand ved lesing, kontroll og skriving av delte variabler.
Begge disse problemene vil bli forklart nedenfor.
Synlighet av delte objekter
Hvis to eller flere tråder deler et objekt uten riktig bruk av flyktig deklarasjon eller synkronisering, kan det hende at endringer i det delte objektet gjort av én tråd ikke er synlige for andre tråder.
Tenk deg at et delt objekt i utgangspunktet er lagret i hovedminnet. En tråd som kjører på en CPU leser det delte objektet inn i hurtigbufferen til samme CPU. Der gjør han endringer på objektet. Inntil CPU-ens hurtigbuffer har blitt tømt til hovedminnet, er den modifiserte versjonen av det delte objektet ikke synlig for tråder som kjører på andre CPUer. Dermed kan hver tråd få sin egen kopi av det delte objektet, hver kopi vil ligge i en egen CPU-cache.
Følgende diagram illustrerer en oversikt over denne situasjonen. Én tråd som kjører på venstre CPU, kopierer det delte objektet inn i cachen og endrer verdien av count til 2. Denne endringen er usynlig for andre tråder som kjører på høyre CPU fordi oppdateringen for å telle ennå ikke er tømt tilbake til hovedminnet.
For å løse dette problemet kan du bruke nøkkelordet volatile når du erklærer en variabel. Den kan sikre at en gitt variabel leses direkte fra hovedminnet og alltid skrives tilbake til hovedminnet når den oppdateres.
Race tilstand
Hvis to eller flere tråder deler det samme objektet og mer enn én tråd oppdaterer variabler i det delte objektet, kan en rasetilstand oppstå.
Tenk deg at tråd A leser det delte objektets tellevariabel inn i prosessorens cache. Tenk deg også at tråd B gjør det samme, men i en annen prosessor sin cache. Nå legger tråd A til 1 til verdien av antall, og tråd B gjør det samme. Nå er variabelen økt to ganger - hver for seg med +1 i hurtigbufferen til hver prosessor.
Hvis disse inkrementene ble utført sekvensielt, ville tellevariabelen bli doblet og skrevet tilbake til hovedminnet (opprinnelig verdi + 2).
Imidlertid ble to inkrementer utført samtidig uten riktig synkronisering. Uavhengig av hvilken tråd (A eller B) som skriver sin oppdaterte versjon av telling til hovedminnet, vil den nye verdien bare være 1 mer enn den opprinnelige verdien, til tross for de to trinnene.
Dette diagrammet illustrerer forekomsten av rasetilstandsproblemet beskrevet ovenfor:
For å løse dette problemet kan du bruke Java-synkronisert blokk. En synkronisert blokk sikrer at bare én tråd kan gå inn i en gitt kritisk del av koden til enhver tid.
Synkroniserte blokker garanterer også at alle variabler som er aksessert inne i den synkroniserte blokken vil bli lest fra hovedminnet, og når tråden går ut av den synkroniserte blokken, vil alle oppdaterte variabler skylles tilbake til hovedminnet, uavhengig av om variabelen er erklært flyktig eller Nei.
GO TO FULL VERSION