Java >> Java tutorial >  >> Tag >> synchronized

Race Condition – Synkroniseret Java Eksempel

I denne artikel diskuterer vi, hvad der er race tilstand og kritiske sektion i Java, og hvordan man forhindrer race tilstand ved hjælp af synkroniseret søgeord ved hjælp af et par eksempler.

Du vil lære:
– Hvad er raceforhold og kritisk sektion?
– Hvordan forhindrer man løbsforhold i Java?
– Hvad er synkronisering i Java, og hvordan bruger man synkroniseret nøgleord til at forhindre racetilstand?

Lignende emner om samtidighed
– Hvad er tråd, og hvordan man opretter en tråd
– CompletableFuture med eksempel
– ConcurrentHashMap med eksempel

Introduktion til kritisk sektion og racetilstand?

package com.codedelay.concurrency;

public class CriticalSectionDemo{
	private int i = 0;
	
	public int incrementValue() {
		System.out.println("Current Thread " + Thread.currentThread().getName());
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return this.i++;
	}
	public static void main(String[] args) { 
		CriticalSectionDemo demo = new CriticalSectionDemo();
		new Thread(() -> demo.incrementValue()).start();
		new Thread(() -> demo.incrementValue()).start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(demo.i);
	}
}

For at forklare det kritiske sektionskoncept, lad os tage et kig på ovenstående CriticalSectionDemo klasse.

I hovedmetoden for CriticalSectionDemo klasse, har vi oprettet to tråde ved hjælp af lambdas-udtryk.

I run() metoden for de tråde, vi kalder incrementValue() metode ved at bruge referencen til CriticalSectionDemo klasse.

I mellemtiden, kunne du observere den problematiske kodeblok i ovenstående eksempel?

Problemet er her.

private int i = 0;
	
	public int incrementValue() {
		System.out.println("Current Thread " + Thread.currentThread().getName());
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return this.i++;
	}

Metoden incrementValue() øger instansvariablen i .

Dog øges værdien i er ikke en atomoperation.

For det første vil den læse værdien af ​​i og derefter vil den øge værdien af ​​i .

Overvej et scenario, hvor en tråd læser værdien af ​​i og samtidig øger andre tråde dens værdi.

Alt taget i betragtning kan vi sige, at this.i++ er ikke trådsikker.

I dette tilfælde blokerer denne kode this.i++ kaldet en kritisk sektion .

Den kritiske sektion af koden er en del af koden, hvor sekvensen af ​​udførelse af forskellige tråde kan ændre programmets forventede adfærd.

Og betingelsen, når flere tråde udfører den kritiske sektion af kode, derefter racetilstand forekommer.

Sådan forhindrer du racetilstand

For at forhindre den kritiske sektion skal vi sørge for, at den kritiske sektionskode skal udføres som en atominstruktion.

Der er to populære måder i Java at forhindre løbstilstanden på.

Synkronisering og ThreadLocal.

Som vi allerede har dækket ThreadLocal i multithreading i java-serien.

I denne øvelse vil vi fokusere på synkronisering ved hjælp af det synkroniserede søgeord.

Synkroniseret søgeord i Java

Synchronized er en modifikator i Java, der bruges til at forhindre løbsforhold.

Dette søgeord gælder kun for metode- og blokniveauet, vi kan ikke bruge synkroniseret til klasser og variabler.

Lad os nu ændre ovenstående kode og tilføje synkroniseret for at forhindre løbstilstanden.

public synchronized int incrementValue() {
		System.out.println("Current Thread " + Thread.currentThread().getName());
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return this.i++;
	}

Nu, hvis du kører programmet, vil du ikke stå over for noget datakonsistensproblem, og uanset trådens udførelsessekvens vil output altid være som forventet.

I ovenstående incrementValue() metode, kun this.i++ er et kritisk afsnit, men vi har låst hele metoden, som ikke er god i den virkelige verden.

Lad os derfor tilføje en synkroniseret blok i ovenstående metode.

public int incrementValue() {
		System.out.println("Current Thread " + Thread.currentThread().getName());
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		synchronized (this) {
			return this.i++;
		}
	}

Sådan fungerer synkronisering

Indtil nu har vi diskuteret, hvordan man bruger synkroniseret søgeord for at undgå løbsforholdene.

Men hvordan fungerer synkronisering?

Lad os tage et kig på intern synkronisering.

Hvert objekt i Java har en unik lås.

Synkronisering bruger dette låsekoncept internt for at undgå løbstilstanden.

Lås på objektniveau

synchronized (this) {
			return this.i++;
		}

I ovenstående eksempel, når en tråd går ind i den synkroniserede blok, så erhverver den først objektet (denne) lås.

Når tråden har fuldført udførelsen, frigiver den låsen.

Men mens en tråd, der udfører den synkroniserede metode/blok og en anden tråd, også ønsker at udføre den samme kodeblok, skal alle disse tråde vente, indtil den første tråd frigiver låsen.

Lås på klasseniveau

I java har hver klasse en unik lås.

Klasseniveaulåse kan anvendes til statisk synkroniserede metoder og blokke.

Derfor, hvis en tråd erhverver klasseniveaulåsen, skal alle andre tråde, der ønsker at erhverve låsen for den klasse, vente, indtil den første tråd frigiver klasseniveaulåsen.

Du skal dog bemærke, at låse på klasseniveau og låse på objektniveau er helt forskellige.

public static int incrementAValue() {
		System.out.println("Current Thread " + Thread.currentThread().getName());
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		synchronized (CriticalSectionDemo.class) {
			return incr++;
		}
	}

Konklusion

I denne tutorial har vi diskuteret de kritiske sektioner og løbsforhold.

Vi har også diskuteret, hvordan man kan forhindre løbsforhold ved hjælp af synkronisering.


Java tag