CodeGym /Java blog /Tilfældig /Bedre sammen: Java og Tråd-klassen. Del VI - Fyr væk!
John Squirrels
Niveau
San Francisco

Bedre sammen: Java og Tråd-klassen. Del VI - Fyr væk!

Udgivet i gruppen

Introduktion

Tråde er en interessant ting. I tidligere anmeldelser har vi set på nogle af de tilgængelige værktøjer til implementering af multithreading. Lad os se, hvilke andre interessante ting vi kan gøre. På dette tidspunkt ved vi meget. For eksempel fra " Bedre sammen: Java og Thread-klassen. Del I — Threads of execution " ved vi, at Thread-klassen repræsenterer en tråd af execution. Vi ved, at en tråd udfører en eller anden opgave. Hvis vi ønsker at vores opgaver skal kunne run, så skal vi markere tråden med Runnable. Bedre sammen: Java og Tråd-klassen.  Del VI - Fyr væk!  - 1For at huske, kan vi bruge Tutorialspoint Online Java Compiler :

public static void main(String[] args){
	Runnable task = () -> {
 		Thread thread = Thread.currentThread();
		System.out.println("Hello from " + thread.getName());
	};
	Thread thread = new Thread(task);
	thread.start();
}
Vi ved også, at vi har noget, der hedder en lås. Vi lærte om dette i " Better together: Java and the Thread class. Part II — Synchronization . Hvis en tråd erhverver en lås, så vil en anden tråd, der forsøger at erhverve låsen, blive tvunget til at vente på, at låsen bliver frigivet:

import java.util.concurrent.locks.*;

public class HelloWorld{
	public static void main(String []args){
		Lock lock = new ReentrantLock();
		Runnable task = () -> {
			lock.lock();
			Thread thread = Thread.currentThread();
			System.out.println("Hello from " + thread.getName());
			lock.unlock();
		};
		Thread thread = new Thread(task);
		thread.start();
	}
}
Jeg synes, det er på tide at tale om, hvilke andre interessante ting vi kan gøre.

Semaforer

Den enkleste måde at kontrollere, hvor mange tråde der kan køre samtidigt, er en semafor. Det er ligesom et jernbanesignal. Grøn betyder fortsæt. Rød betyder vent. Vent på hvad fra semaforen? Adgang. For at få adgang skal vi anskaffe den. Og når adgangen ikke længere er nødvendig, skal vi give den væk eller frigive den. Lad os se, hvordan dette virker. Vi skal importere java.util.concurrent.Semaphoreklassen. Eksempel:

public static void main(String[] args) throws InterruptedException {
	Semaphore semaphore = new Semaphore(0);
	Runnable task = () -> {
		try {
			semaphore.acquire();
			System.out.println("Finished");
			semaphore.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	Thread.sleep(5000);
	semaphore.release(1);
}
Som du kan se, hjælper disse operationer (anskaffelse og frigivelse) os med at forstå, hvordan en semafor fungerer. Det vigtigste er, at hvis vi skal få adgang, så skal semaforen have et positivt antal tilladelser. Denne optælling kan initialiseres til et negativt tal. Og vi kan anmode om (erhverve) mere end 1 tilladelse.

CountDownLatch

Den næste mekanisme er CountDownLatch. Ikke overraskende er dette en lås med en nedtælling. Her skal vi bruge den passende importerklæring til java.util.concurrent.CountDownLatchklassen. Det er ligesom et fodløb, hvor alle samles ved startlinjen. Og når alle er klar, modtager alle startsignalet på samme tid og starter samtidigt. Eksempel:

public static void main(String[] args) {
	CountDownLatch countDownLatch = new CountDownLatch(3);
	Runnable task = () -> {
		try {
			countDownLatch.countDown();
			System.out.println("Countdown: " + countDownLatch.getCount());
			countDownLatch.await();
			System.out.println("Finished");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
 	}
}
Først fortæller vi først låsen til countDown(). Google definerer nedtælling som "en handling med at tælle tal i omvendt rækkefølge til nul". Og så fortæller vi låsen til await(), altså vente til tælleren bliver nul. Interessant nok er dette en engangstæller. Java-dokumentationen siger, "Når tråde gentagne gange skal tælle ned på denne måde, så brug i stedet en CyclicBarrier". Med andre ord, hvis du har brug for en genanvendelig tæller, har du brug for en anden mulighed: CyclicBarrier.

CyclicBarrier

Som navnet antyder, CyclicBarrierer en "genanvendelig" barriere. Vi bliver nødt til at importere java.util.concurrent.CyclicBarrierklassen. Lad os se på et eksempel:

public static void main(String[] args) throws InterruptedException {
	Runnable action = () -> System.out.println("On your mark!");
	CyclicBarrier barrier = new CyclicBarrier(3, action);
	Runnable task = () -> {
		try {
			barrier.await();
			System.out.println("Finished");
		} catch (BrokenBarrierException | InterruptedException e) {
			e.printStackTrace();
		}
	};
	System.out.println("Limit: " + barrier.getParties());
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
	}
}
Som du kan se, kører tråden awaitmetoden, dvs den venter. I dette tilfælde falder barriereværdien. Barrieren anses for brudt ( barrier.isBroken()), når nedtællingen når nul. For at nulstille barrieren skal du kalde reset()metoden, som CountDownLatchikke har.

Udveksler

Den næste mekanisme er Exchanger. I denne sammenhæng er en Exchange et synkroniseringspunkt, hvor ting ændres udveksles eller byttes. Som du ville forvente, Exchangerer an en klasse, der udfører en udveksling eller swap. Lad os se på det enkleste eksempel:

public static void main(String[] args) {
	Exchanger<String> exchanger = new Exchanger<>();
	Runnable task = () -> {
		try {
			Thread thread = Thread.currentThread();
			String withThreadName = exchanger.exchange(thread.getName());
			System.out.println(thread.getName() + " exchanged with " + withThreadName);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	new Thread(task).start();
}
Her starter vi to tråde. Hver af dem kører udvekslingsmetoden og venter på, at den anden tråd også kører udvekslingsmetoden. Derved udveksler trådene de beståede argumenter. Interessant. Minder det dig ikke om noget? Det minder om SynchronousQueue, som ligger i hjertet af CachedThreadPool. For klarhedens skyld er her et eksempel:

public static void main(String[] args) throws InterruptedException {
	SynchronousQueue<String> queue = new SynchronousQueue<>();
	Runnable task = () -> {
		try {
			System.out.println(queue.take());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	queue.put("Message");
}
Eksemplet viser, at når en ny tråd startes, vil den vente, fordi køen vil være tom. Og så sætter hovedtråden "Besked"-strengen ind i køen. Hvad mere er, vil den også stoppe, indtil denne streng modtages fra køen. Du kan også læse " SynchronousQueue vs Exchanger " for at finde mere om dette emne.

Phaser

Vi har gemt det bedste til sidst — Phaser. Vi bliver nødt til at importere java.util.concurrent.Phaserklassen. Lad os se på et simpelt eksempel:

public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser();
        // By calling the register method, we register the current (main) thread as a party
        phaser.register();
        System.out.println("Phasecount is " + phaser.getPhase());
        testPhaser(phaser);
        testPhaser(phaser);
        testPhaser(phaser);
        // After 3 seconds, we arrive at the barrier and deregister. Number of arrivals = number of registrations = start
        Thread.sleep(3000);
        phaser.arriveAndDeregister();
        System.out.println("Phasecount is " + phaser.getPhase());
    }

    private static void testPhaser(final Phaser phaser) {
        // We indicate that there will be a +1 party on the Phaser
        phaser.register();
        // Start a new thread
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + " arrived");
            phaser.arriveAndAwaitAdvance(); // The threads register arrival at the phaser.
            System.out.println(name + " after passing barrier");
        }).start();
    }
Eksemplet illustrerer, at ved brug af Phaser, knækker bommen, når antallet af registreringer matcher antallet af ankomster til bommen. Du kan blive mere fortrolig med Phaserved at læse denne GeeksforGeeks artikel .

Resumé

Som du kan se fra disse eksempler, er der forskellige måder at synkronisere tråde på. Tidligere prøvede jeg at huske aspekter af multithreading. Jeg håber, at de tidligere afsnit i denne serie var nyttige. Nogle mennesker siger, at vejen til multithreading begynder med bogen "Java Concurrency in Practice". Selvom den blev udgivet i 2006, siger folk, at bogen er ret grundlæggende og stadig relevant i dag. Du kan for eksempel læse diskussionen her: Er "Java Concurrency In Practice" stadig gyldig? . Det er også nyttigt at læse links i diskussionen. For eksempel er der et link til bogen The Well-Grounded Java Developer , og vi vil især nævne kapitel 4. Modern concurrency . Der er også en hel anmeldelse om dette emne:Er "Java Concurrency in Practice" stadig gyldig i Java 8-æraen? Denne artikel giver også forslag til, hvad du ellers skal læse for virkelig at forstå dette emne. Derefter kan du tage et kig på en fantastisk bog som OCA/OCP Java SE 8 Programmer Practice Tests . Vi er interesserede i det andet akronym: OCP (Oracle Certified Professional). Du finder tests i "Kapitel 20: Java Concurrency". Denne bog har både spørgsmål og svar med forklaringer. For eksempel: Bedre sammen: Java og Tråd-klassen.  Del VI - Fyr væk!  - 3Mange mennesker begynder måske at sige, at dette spørgsmål er endnu et eksempel på memorisering af metoder. På den ene side, ja. På den anden side kan du besvare dette spørgsmål ved at huske, at det ExecutorServiceer en slags "opgradering" af Executor. OgExecutorer beregnet til blot at skjule den måde, tråde oprettes på, men det er ikke den primære måde at udføre dem på, dvs. starte et Runnableobjekt på en ny tråd. Derfor er der ingen execute(Callable)— fordi i tilføjer ExecutorServiceden Executorblot submit()metoder, der kan returnere et Futureobjekt. Selvfølgelig kan vi huske en liste over metoder udenad, men det er meget nemmere at lave vores svar baseret på vores viden om karakteren af ​​selve klasserne. Og her er nogle yderligere materialer om emnet: Bedre sammen: Java og Tråd-klassen. Del I — Udførelsestråde Bedre sammen: Java og trådklassen. Del II — Synkronisering Bedre sammen: Java og Thread-klassen. Del III — Interaktion Bedre sammen: Java og Thread-klassen. Del IV — Callable, Future og friends Bedre sammen: Java og Thread-klassen. Del V — Executor, ThreadPool, Fork/Join
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION