java synchronized what is thread synchronization java
Deze tutorial legt de thread-synchronisatie in Java uit, samen met gerelateerde concepten zoals Java Lock, Race Condition, Mutex, Java Volatile & Deadlock in Java:
In een multithreading-omgeving waar meerdere threads bij betrokken zijn, zijn er ongetwijfeld botsingen wanneer meer dan één thread tegelijkertijd dezelfde resource probeert te krijgen. Deze botsingen resulteren in 'race condition' en dus levert het programma onverwachte resultaten op.
Bijvoorbeeld, een enkel bestand wordt bijgewerkt door twee threads. Als een thread T1 bezig is met het bijwerken van dit bestand, zeg dan een variabele. Terwijl deze update door T1 nog aan de gang is, laten we zeggen dat de tweede thread T2 ook dezelfde variabele bijwerkt. Op deze manier geeft de variabele verkeerde resultaten.
Bekijk hier de complete Java-trainingsserie.
Als er meerdere threads bij betrokken zijn, moeten we deze threads zo beheren dat een bron door één thread tegelijk kan worden benaderd. In het bovenstaande voorbeeld moet het bestand waartoe beide threads toegang hebben, zodanig worden beheerd dat T2 geen toegang tot het bestand kan krijgen totdat T1 er geen toegang toe heeft.
Dit wordt gedaan in Java met “ Thread-synchronisatie
Wat je leert:
- Thread-synchronisatie in Java
- Multi-threading zonder synchronisatie
- Multi-threading met synchronisatie
- Gevolgtrekking
Thread-synchronisatie in Java
Aangezien Java een taal met meerdere threads is, is synchronisatie van threads van groot belang in Java, aangezien meerdere threads parallel worden uitgevoerd in een toepassing.
We gebruiken trefwoorden 'Gesynchroniseerd' en 'Vluchtig' om synchronisatie in Java te bereiken
We hebben synchronisatie nodig wanneer het gedeelde object of de bron veranderlijk is. Als de bron onveranderlijk is, zullen de threads de bron alleen gelijktijdig of afzonderlijk lezen.
In dit geval hoeven we de bron niet te synchroniseren. In dit geval zorgt JVM ervoor dat Gesynchroniseerde Java-code wordt door één thread tegelijk uitgevoerd
In de meeste gevallen kan gelijktijdige toegang tot gedeelde bronnen in Java fouten introduceren zoals 'geheugeninconsistentie' en 'threadinterferentie'. Om deze fouten te vermijden, moeten we gaan voor synchronisatie van gedeelde bronnen, zodat de toegang tot deze bronnen elkaar uitsluiten.
We gebruiken een concept genaamd Monitoren om synchronisatie te implementeren. Een monitor kan slechts door één thread tegelijk worden geopend. Wanneer een draad de vergrendeling krijgt, kunnen we zeggen dat de draad de monitor is binnengekomen.
Wanneer een monitor wordt benaderd door een bepaalde thread, wordt de monitor vergrendeld en worden alle andere threads die de monitor proberen binnen te komen opgeschort totdat de toegangsdraad is voltooid en wordt de vergrendeling vrijgegeven.
In de toekomst zullen we synchronisatie in Java in deze tutorial in detail bespreken. Laten we nu enkele basisconcepten bespreken met betrekking tot synchronisatie in Java.
Raceconditie in Java
Wanneer in een omgeving met meerdere threads meer dan één thread probeert toegang te krijgen tot een gedeelde bron om tegelijkertijd te schrijven, racen meerdere threads tegen elkaar om de toegang tot de bron te voltooien. Dit geeft aanleiding tot ‘race condition’.
Een ding om te overwegen is dat er geen probleem is als meerdere threads alleen proberen toegang te krijgen tot een gedeelde bron om te lezen. Het probleem doet zich voor wanneer meerdere threads tegelijkertijd toegang hebben tot dezelfde bron.
Race-omstandigheden treden op vanwege een gebrek aan goede synchronisatie van threads in het programma. Als we de threads op de juiste manier synchroniseren, zodat slechts één thread tegelijkertijd toegang heeft tot de bron en de racevoorwaarde ophoudt te bestaan.
Dus hoe kunnen we de raceconditie detecteren?
De beste manier om racecondities te detecteren, is door middel van codebeoordeling. Als programmeur moeten we de code grondig doornemen om te controleren op mogelijke race-omstandigheden die kunnen optreden.
Sloten / monitoren in Java
We hebben al vermeld dat we monitoren of vergrendelingen gebruiken om synchronisatie te implementeren. De monitor of het slot is een interne entiteit en is geassocieerd met elk object. Dus wanneer een thread toegang moet hebben tot het object, moet deze eerst het slot of de monitor van zijn object bemachtigen, aan het object werken en vervolgens het slot loslaten.
Vergrendelingen in Java zien er als volgt uit:
Zoals hierboven weergegeven, hebben we een methode lock () die de instantie vergrendelt. Alle threads die de methode lock () aanroepen, worden geblokkeerd totdat de sets van de methode unblock () de vlag op false hebben vergrendeld en alle wachtende threads op de hoogte stellen.
Enkele tips om te onthouden over sloten:
- In Java heeft elk object een slot of een monitor. Dit slot is toegankelijk via een schroefdraad.
- Per keer kan slechts één thread deze monitor of vergrendeling verkrijgen.
- De programmeertaal Java biedt een trefwoord Synchronized 'waarmee we de threads kunnen synchroniseren door een blok of methode als Synchronized te maken.
- De gedeelde bronnen die de threads nodig hebben om toegang te krijgen, worden bewaard onder deze gesynchroniseerde blok / methode.
Mutexes in Java
We hebben al besproken dat in een omgeving met meerdere threads racecondities kunnen optreden wanneer meer dan één thread tegelijkertijd probeert toegang te krijgen tot de gedeelde bronnen en de racecondities resulteren in onverwachte uitvoer.
Het deel van het programma dat toegang probeert te krijgen tot de gedeelde bron wordt de 'Kritieke sectie' Om het optreden van race-omstandigheden te voorkomen, is het nodig om de toegang tot de kritieke sectie te synchroniseren. Door deze kritieke sectie te synchroniseren, zorgen we ervoor dat slechts één thread tegelijk toegang heeft tot de kritieke sectie.
Het eenvoudigste type synchronisator is de 'mutex'. Mutex zorgt ervoor dat op een gegeven moment slechts één thread de kritieke sectie kan uitvoeren.
De mutex is vergelijkbaar met het concept van monitoren of vergrendelingen dat we hierboven hebben besproken. Als een thread toegang moet hebben tot een kritieke sectie, moet deze de mutex. Zodra mutex is verkregen, krijgt de thread toegang tot de kritieke sectiecode en als dit klaar is, wordt de mutex vrijgegeven.
De andere threads die wachten om toegang te krijgen tot de kritieke sectie, worden in de tussentijd geblokkeerd. Zodra de thread die mutex vasthoudt, deze loslaat, komt een andere thread in de kritieke sectie.
hoe u een .dat-bestand kunt bekijken
Er zijn verschillende manieren waarop we een mutex in Java kunnen implementeren.
- Gesynchroniseerd trefwoord gebruiken
- Semaphore gebruiken
- Met behulp van ReentrantLock
In deze tutorial bespreken we de eerste benadering, d.w.z. synchronisatie. De andere twee benaderingen - Semaphore en ReentrantLock zullen worden besproken in de volgende tutorial waarin we het java concurrent-pakket zullen bespreken.
Gesynchroniseerd trefwoord
Java biedt een trefwoord 'Gesynchroniseerd' dat in een programma kan worden gebruikt om een kritieke sectie te markeren. De kritieke sectie kan een blok code of een complete methode zijn. Slechts één thread heeft dus toegang tot de kritieke sectie die is gemarkeerd met het trefwoord Synchronized.
We kunnen de gelijktijdige delen (delen die gelijktijdig worden uitgevoerd) voor een applicatie schrijven met behulp van het trefwoord Synchronized. We verwijderen ook de racecondities door een codeblok of een methode Synchronized te maken.
Wanneer we een blok of methode als gesynchroniseerd markeren, beschermen we de gedeelde bronnen binnen deze entiteiten tegen gelijktijdige toegang en daardoor tegen beschadiging.
Soorten synchronisatie
Er zijn 2 soorten synchronisatie, zoals hieronder uitgelegd:
# 1) Processynchronisatie
Processynchronisatie houdt in dat meerdere processen of threads tegelijkertijd worden uitgevoerd. Ze bereiken uiteindelijk een toestand waarin deze processen of threads zich verbinden tot een specifieke reeks acties.
# 2) Thread-synchronisatie
Bij Thread Synchronization proberen meer dan één thread toegang te krijgen tot een gedeelde ruimte. De threads worden zo gesynchroniseerd dat de gedeelde ruimte slechts voor één thread tegelijk toegankelijk is.
De processynchronisatie valt buiten het bestek van deze tutorial. Daarom zullen we hier alleen Thread Synchronization bespreken.
In Java kunnen we het gesynchroniseerde trefwoord gebruiken met:
- Een blok code
- Een methode
De bovenstaande typen zijn de elkaar uitsluitende typen threadsynchronisatie. Wederzijdse uitsluiting zorgt ervoor dat de threads die toegang hebben tot gedeelde gegevens elkaar niet hinderen.
Het andere type threadsynchronisatie is 'InterThread-communicatie' die is gebaseerd op samenwerking tussen threads. Interthread-communicatie valt buiten het bestek van deze tutorial.
Voordat we doorgaan met de synchronisatie van blokken en methoden, moeten we een Java-programma implementeren om het gedrag van threads te demonstreren wanneer er geen synchronisatie is.
Multi-threading zonder synchronisatie
Het volgende Java-programma heeft meerdere threads die niet zijn gesynchroniseerd.
Uitvoer
Aan de output kunnen we zien dat, aangezien de threads niet gesynchroniseerd zijn, de output inconsistent is. Beide threads starten en vervolgens geven ze de teller een voor een weer. Beide draden worden aan het einde afgesloten.
Vanuit het gegeven programma zou de eerste thread moeten zijn afgesloten na het weergeven van de tellerwaarden, en dan zou de tweede thread moeten zijn begonnen met het weergeven van de tellerwaarden.
Laten we nu gaan voor synchronisatie en beginnen met synchronisatie van codeblokjes.
Gesynchroniseerd codeblok
Een gesynchroniseerd blok wordt gebruikt om een codeblok te synchroniseren. Dit blok bestaat meestal uit een paar regels. Een gesynchroniseerd blok wordt gebruikt als we niet willen dat een hele methode wordt gesynchroniseerd.
Bijvoorbeeld, we hebben een methode met zeg maar 75 regels code. Hiervan hoeven slechts 10 regels code te worden uitgevoerd door één thread tegelijk. Als we in dit geval de hele methode gesynchroniseerd maken, zal dit een belasting voor het systeem zijn. In dergelijke situaties gaan we voor gesynchroniseerde blokken.
De omvang van de gesynchroniseerde methode is altijd kleiner dan die van een gesynchroniseerde methode. Een gesynchroniseerde methode vergrendelt een object van een gedeelde bron dat door meerdere threads moet worden gebruikt.
De algemene syntaxis van een gesynchroniseerd blok is zoals hieronder weergegeven:
Hier is 'lock_object' een objectreferentie-uitdrukking waarop het slot moet worden verkregen. Dus wanneer een thread toegang wil hebben tot de gesynchroniseerde instructies in het blok voor uitvoering, moet deze de vergrendeling op de ‘lock_object’ -monitor verwerven.
Zoals reeds besproken, zorgt het gesynchroniseerde sleutelwoord ervoor dat slechts één draad tegelijk een vergrendeling kan krijgen en alle andere draden moeten wachten tot de draad die de vergrendeling vasthoudt, is voltooid en de vergrendeling vrijgeeft.
Notitie
- Een 'NullPointerException' wordt gegenereerd als het gebruikte lock_object Null is.
- Slaapt een draad terwijl u de vergrendeling nog vasthoudt, dan wordt de vergrendeling niet vrijgegeven. De andere threads hebben tijdens deze slaaptijd geen toegang tot het gedeelde object.
Nu zullen we het bovenstaande voorbeeld presenteren dat al met kleine wijzigingen was geïmplementeerd. In het eerdere programma hebben we de code niet gesynchroniseerd. Nu zullen we het gesynchroniseerde blok gebruiken en de uitvoer vergelijken.
Multi-threading met synchronisatie
In het onderstaande Java-programma gebruiken we een gesynchroniseerd blok. In de run-methode synchroniseren we de code van regels die de teller voor elke thread afdrukken.
Uitvoer
Nu is de uitvoer van dit programma met behulp van gesynchroniseerd blok vrij consistent. Zoals verwacht worden beide threads uitgevoerd. De eerste thread is klaar met het weergeven van de tellerwaarden en gaat weg. Vervolgens geeft de tweede thread de tellerwaarden weer en wordt afgesloten.
Gesynchroniseerde methode
Laten we de gesynchroniseerde methode in dit gedeelte bespreken. Eerder hebben we gezien dat we een klein blok bestaande uit minder coderegels kunnen declareren als een gesynchroniseerd blok. Als we willen dat de hele functie wordt gesynchroniseerd, kunnen we een methode als gesynchroniseerd declareren.
Wanneer een methode gesynchroniseerd is gemaakt, kan slechts één thread tegelijk een methodeaanroep maken.
De algemene syntaxis voor het schrijven van een gesynchroniseerde methode is:
Net als bij een gesynchroniseerd blok hebben we in het geval van een gesynchroniseerde methode een lock_object nodig dat wordt gebruikt door threads die toegang hebben tot de gesynchroniseerde methode.
Voor de gesynchroniseerde methode kan het vergrendelingsobject een van de volgende zijn:
- Als de gesynchroniseerde methode statisch is, wordt het vergrendelingsobject gegeven door ‘.class’ -object.
- Voor een niet-statische methode wordt het vergrendelingsobject gegeven door het huidige object, d.w.z. ‘dit’ object.
Een bijzonder kenmerk van het gesynchroniseerde sleutelwoord is dat het opnieuw binnenkomt. Dit betekent dat een gesynchroniseerde methode een andere gesynchroniseerde methode met hetzelfde slot kan aanroepen. Dus een draad die het slot vasthoudt, heeft toegang tot een andere gesynchroniseerde methode zonder een ander slot te hoeven verwerven.
De gesynchroniseerde methode wordt gedemonstreerd aan de hand van het onderstaande voorbeeld.
Uitvoer
In het bovenstaande programma hebben we een gesynchroniseerde methode gebruikt om de vierkantjes van een getal af te drukken. De bovengrens van het aantal wordt als argument aan de methode doorgegeven. Beginnend bij 1, worden de vierkanten van elk nummer afgedrukt totdat de bovengrens is bereikt.
In de hoofdfunctie wordt de threadinstantie gemaakt. Elke threadinstantie krijgt een nummer om vierkantjes af te drukken.
Zoals hierboven vermeld, wanneer een te synchroniseren methode statisch is, is het lock-object betrokken bij de klasse en niet bij het object. Dit betekent dat we de klasse vergrendelen en niet het object. Dit wordt statische synchronisatie genoemd.
Een ander voorbeeld wordt hieronder gegeven.
Uitvoer
In het bovenstaande programma printen we tafels van vermenigvuldiging met getallen. Elk nummer waarvan de tabel moet worden afgedrukt, is een threadinstantie van een andere threadklasse. Dus we printen de tafels van vermenigvuldiging van 2 & 5, dus we hebben twee klassen ’thread_one en thread_two om respectievelijk de tabellen 2 en 5 af te drukken.
Samenvattend voert het Java-gesynchroniseerde sleutelwoord de volgende functies uit:
dataprovider website voor online deals
- Het gesynchroniseerde sleutelwoord in Java garandeert wederzijds exclusieve toegang tot gedeelde bronnen door een vergrendelingsmechanisme te bieden. Vergrendeling voorkomt ook racecondities.
- Met het gesynchroniseerde trefwoord voorkomen we gelijktijdige programmeerfouten in de code.
- Wanneer een methode of blok als gesynchroniseerd wordt gedeclareerd, heeft een thread een exclusieve vergrendeling nodig om de gesynchroniseerde methode of het blok in te voeren. Na het uitvoeren van de nodige acties, geeft de draad de vergrendeling vrij en wordt de schrijfbewerking doorgespoeld. Op deze manier elimineert het geheugenfouten die verband houden met inconsistentie.
Vluchtig in Java
Een vluchtig trefwoord in Java wordt gebruikt om klassen thread-safe te maken. We gebruiken ook het vluchtige sleutelwoord om de variabele waarde door verschillende threads te wijzigen. Een vluchtig trefwoord kan worden gebruikt om een variabele met zowel primitieve typen als objecten te declareren.
In bepaalde gevallen wordt een vluchtig trefwoord gebruikt als alternatief voor het gesynchroniseerde trefwoord, maar houd er rekening mee dat het geen vervanging is voor het gesynchroniseerde trefwoord.
Als een variabele vluchtig wordt verklaard, wordt de waarde ervan nooit in de cache opgeslagen, maar altijd uit het hoofdgeheugen gelezen. Een vluchtige variabele garandeert ordening en zichtbaarheid. Hoewel een variabele als vluchtig kan worden gedeclareerd, kunnen we klassen of methoden niet als vluchtig verklaren.
Beschouw het volgende codeblok:
In de bovenstaande code is de variabele myvar statisch en vluchtig. Een statische variabele wordt gedeeld door alle klasseobjecten. De vluchtige variabele bevindt zich altijd in het hoofdgeheugen en wordt nooit in de cache opgeslagen.
Daarom zal er slechts één kopie van myvar in het hoofdgeheugen zijn en zullen alle lees- / schrijfacties worden uitgevoerd op deze variabele vanuit het hoofdgeheugen. Als myvar niet als vluchtig werd gedeclareerd, zou elk thread-object een andere kopie hebben die tot inconsistenties zou leiden.
Enkele van de verschillen tussen vluchtige en gesynchroniseerde trefwoorden worden hieronder vermeld.
Vluchtig trefwoord | Gesynchroniseerd trefwoord |
---|---|
Het vluchtige trefwoord wordt alleen met variabelen gebruikt. | Het gesynchroniseerde trefwoord wordt gebruikt met codeblokken en methoden. |
Een vluchtig trefwoord kan de thread niet blokkeren om te wachten. | Het gesynchroniseerde trefwoord kan de thread blokkeren om te wachten. |
De draadprestaties zijn verbeterd met Volatile. | De draadprestaties nemen enigszins af met gesynchroniseerd. |
Vluchtige variabelen bevinden zich in het hoofdgeheugen. | Gesynchroniseerde constructies bevinden zich niet in het hoofdgeheugen. |
Vluchtig synchroniseert één variabele tegelijk tussen threadgeheugen en hoofdgeheugen. | Gesynchroniseerd sleutelwoord synchroniseert alle variabelen in één keer. |
Deadlock in Java
We hebben gezien dat we meerdere threads kunnen synchroniseren met behulp van gesynchroniseerde trefwoorden en programma's thread-safe kunnen maken. Door de threads te synchroniseren zorgen we ervoor dat de meerdere threads tegelijkertijd worden uitgevoerd in een multi-threaded omgeving.
Soms doet zich echter een situatie voor waarin threads niet meer tegelijkertijd kunnen functioneren. In plaats daarvan wachten ze eindeloos. Dit gebeurt wanneer een thread wacht op een resource en die resource wordt geblokkeerd door de tweede thread.
De tweede thread, aan de andere kant, wacht op de bron die wordt geblokkeerd door de eerste thread. Een dergelijke situatie leidt tot een 'impasse' in Java.
Deadlock in Java wordt weergegeven met behulp van de onderstaande afbeelding.
Zoals we kunnen zien in het bovenstaande diagram, heeft thread A de resource r1 vergrendeld en wacht op resource r2. Thread B, aan de andere kant, heeft bron r2 geblokkeerd en wacht op r1.
Dus geen van de threads kan hun uitvoering voltooien, tenzij ze de hangende bronnen in handen krijgen. Deze situatie heeft geresulteerd in een impasse waarin beide threads eindeloos wachten op de bronnen.
Hieronder is een voorbeeld van Deadlocks in Java gegeven.
Uitvoer
In het bovenstaande programma hebben we twee gedeelde bronnen en twee threads. Beide threads proberen één voor één toegang te krijgen tot de gedeelde bronnen. De uitvoer toont beide threads die elk één bron vergrendelen terwijl ze op de andere wachten. Daardoor ontstaat een impasse.
Hoewel we impassesituaties niet volledig kunnen voorkomen, kunnen we ze zeker vermijden door enkele maatregelen te nemen.
Hieronder staan de middelen vermeld waarmee we impasses in Java kunnen voorkomen.
# 1) Door geneste vergrendelingen te vermijden
Het hebben van geneste sloten is de belangrijkste reden voor impasses. Geneste vergrendelingen zijn de vergrendelingen die aan meerdere threads worden gegeven. We moeten dus vermijden om aan meer dan één thread locks te geven.
# 2) Gebruik thread Join
We zouden Thread.join met maximale tijd moeten gebruiken, zodat de threads de maximale tijd voor uitvoering kunnen gebruiken. Dit voorkomt een impasse die meestal optreedt als de ene thread continu op de andere wacht.
# 3) Vermijd onnodige vergrendeling
We moeten alleen de benodigde code vergrendelen. Het hebben van onnodige vergrendelingen voor de code kan leiden tot impasses in het programma. Omdat impasses de code kunnen breken en de doorstroming van het programma kunnen belemmeren, moeten we geneigd zijn impasses in onze programma's te vermijden.
Veel Gestelde Vragen
V # 1) Wat is synchronisatie en waarom is het belangrijk?
Antwoord: Synchronisatie is het proces waarbij de toegang van een gedeelde bron tot meerdere threads wordt beheerd. Zonder synchronisatie kunnen meerdere threads de gedeelde bron tegelijkertijd bijwerken of wijzigen, wat tot inconsistenties leidt.
We moeten er dus voor zorgen dat in een omgeving met meerdere threads de threads worden gesynchroniseerd, zodat de manier waarop ze toegang krijgen tot de gedeelde bronnen wederzijds exclusief en consistent is.
Vraag 2) Wat is synchronisatie en niet-synchronisatie in Java?
Antwoord: Synchronisatie betekent dat een construct een thread-safe is. Dit betekent dat meerdere threads niet tegelijk toegang hebben tot de constructie (codeblok, methode, enz.).
Niet-gesynchroniseerde constructies zijn niet thread-safe. Meerdere threads hebben op elk moment toegang tot de niet-gesynchroniseerde methoden of blokken. Een populaire niet-gesynchroniseerde klasse in Java is StringBuilder.
V # 3) Waarom is synchronisatie vereist?
Antwoord: Wanneer processen gelijktijdig moeten worden uitgevoerd, hebben we synchronisatie nodig. Dit komt omdat we bronnen nodig hebben die door veel processen kunnen worden gedeeld.
Om botsingen tussen processen of threads voor toegang tot gedeelde bronnen te voorkomen, moeten we deze bronnen synchroniseren, zodat alle threads toegang krijgen tot bronnen en de applicatie ook soepel verloopt.
V # 4) Hoe kom je aan een gesynchroniseerde arraylijst?
Antwoord: We kunnen de methode Collections.synchronized list gebruiken met ArrayList als een argument om ArrayList naar een gesynchroniseerde lijst te converteren.
V # 5) Is HashMap gesynchroniseerd?
salesforce admin interviewvragen en antwoorden pdf
Antwoord: Nee, HashMap is niet gesynchroniseerd, maar HashTable is wel gesynchroniseerd.
Gevolgtrekking
In deze tutorial hebben we de synchronisatie van threads in detail besproken. Daarnaast leerden we ook over het vluchtige sleutelwoord en de impasses in Java. Synchronisatie bestaat uit proces- en threadsynchronisatie.
In een multithreading-omgeving houden we ons meer bezig met thread-synchronisatie. We hebben hier de gesynchroniseerde trefwoordbenadering van threadsynchronisatie gezien.
Deadlock is een situatie waarin meerdere threads eindeloos wachten op bronnen. We hebben het voorbeeld van deadlocks in Java gezien, samen met de methoden om impasses in Java te voorkomen.
Bezoek hier om Java vanaf het begin te leren.
Aanbevolen literatuur
- Thread.Sleep () - Thread Sleep () -methode in Java met voorbeelden
- Java-threads met methoden en levenscyclus
- Java Basics: Java Syntax, Java Class en Core Java Concepts
- Multithreading in Java - Tutorial met voorbeelden
- Multithreading in C ++ met voorbeelden
- JAVA-zelfstudie voor beginners: 100+ praktische Java-videotutorials
- Java-componenten: Java-platform, JDK, JRE en Java Virtual Machine
- Zelfstudie over Java String | Java String-methoden met voorbeelden