CodeGym /Java blogg /Slumpmässig /Bättre tillsammans: Java och trådklassen. Del VI — Skjut ...
John Squirrels
Nivå
San Francisco

Bättre tillsammans: Java och trådklassen. Del VI — Skjut loss!

Publicerad i gruppen

Introduktion

Trådar är en intressant sak. I tidigare recensioner tittade vi på några av de tillgängliga verktygen för att implementera multithreading. Låt oss se vilka andra intressanta saker vi kan göra. Vid det här laget vet vi mycket. Till exempel, från " Bättre tillsammans: Java och klassen Thread. Del I — Threads of execution " vet vi att Thread-klassen representerar en exekveringstråd. Vi vet att en tråd utför någon uppgift. Om vi ​​vill att våra uppgifter ska kunna runså måste vi markera tråden med Runnable. Bättre tillsammans: Java och trådklassen.  Del VI — Skjut loss!  - 1För att komma ihåg kan vi använda 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 vet också att vi har något som kallas lås. Vi lärde oss om detta i " Bättre tillsammans: Java och trådklassen. Del II — Synkronisering . Om en tråd skaffar ett lås, kommer en annan tråd som försöker få låset att tvingas vänta på att låset släpps:

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();
	}
}
Jag tycker att det är dags att prata om vilka andra intressanta saker vi kan göra.

Semaforer

Det enklaste sättet att kontrollera hur många trådar som kan köras samtidigt är en semafor. Det är som en järnvägssignal. Grönt betyder fortsätt. Rött betyder vänta. Vänta på vad från semaforen? Tillgång. För att få tillgång måste vi skaffa det. Och när åtkomst inte längre behövs måste vi ge bort eller släppa det. Låt oss se hur det här fungerar. Vi måste importera java.util.concurrent.Semaphoreklassen. Exempel:

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 dessa operationer (förvärva och släpp) oss att förstå hur en semafor fungerar. Det viktigaste är att om vi ska få tillträde så måste semaforen ha ett positivt antal tillstånd. Detta antal kan initieras till ett negativt tal. Och vi kan begära (förvärva) mer än 1 tillstånd.

CountdownLatch

Nästa mekanism är CountDownLatch. Föga överraskande är detta en spärr med nedräkning. Här behöver vi lämplig importsats för klassen java.util.concurrent.CountDownLatch. Det är som ett fotlopp, där alla samlas vid startlinjen. Och när alla är redo får alla startsignalen samtidigt och startar samtidigt. Exempel:

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 säger vi först till spärren att countDown(). Google definierar nedräkning som "en handling av att räkna siffror i omvänd ordning till noll". Och sedan säger vi till spärren till await(), dvs vänta tills räknaren blir noll. Intressant nog är detta en engångsräknare. Java-dokumentationen säger, "När trådar upprepade gånger måste räkna ner på detta sätt, använd istället en CyclicBarrier". Med andra ord, om du behöver en återanvändbar disk behöver du ett annat alternativ: CyclicBarrier.

CyclicBarrier

Som namnet antyder, CyclicBarrierär en "återanvändbar" barriär. Vi måste importera java.util.concurrent.CyclicBarrierklassen. Låt oss titta på ett exempel:

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 ni ser kör tråden awaitmetoden, dvs den väntar. I detta fall minskar barriärvärdet. Barriären anses bruten ( barrier.isBroken()) när nedräkningen når noll. För att återställa barriären måste du anropa reset()metoden, som CountDownLatchinte har.

Växlare

Nästa mekanism är Exchanger. I det här sammanhanget är en Exchange en synkroniseringspunkt där saker som förändras kan bytas eller bytas. Som du kan förvänta dig Exchangerär an en klass som utför ett utbyte eller swap. Låt oss titta på det enklaste exemplet:

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();
}
Här startar vi två trådar. Var och en av dem kör utbytesmetoden och väntar på att den andra tråden också kör utbytesmetoden. På så sätt utbyter trådarna de godkända argumenten. Intressant. Påminner det dig inte om något? Det påminner om SynchronousQueue, som ligger i hjärtat av CachedThreadPool. För tydlighetens skull, här är ett exempel:

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");
}
Exemplet visar att när en ny tråd startas kommer den att vänta, eftersom kön blir tom. Och sedan lägger huvudtråden "Meddelande"-strängen i kön. Dessutom kommer den också att stanna tills den här strängen tas emot från kön. Du kan också läsa " SynchronousQueue vs Exchanger " för att hitta mer om detta ämne.

Phaser

Vi har sparat det bästa till sist — Phaser. Vi måste importera java.util.concurrent.Phaserklassen. Låt oss titta på ett enkelt exempel:

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();
    }
Exemplet illustrerar att när man använder Phaser, spricker bommen när antalet registreringar matchar antalet ankomster till bommen. Du kan bli mer bekant med Phasergenom att läsa denna GeeksforGeeks-artikel .

Sammanfattning

Som du kan se av dessa exempel finns det olika sätt att synkronisera trådar. Tidigare försökte jag komma ihåg aspekter av multithreading. Jag hoppas att de tidigare avsnitten i den här serien var användbara. Vissa säger att vägen till multithreading börjar med boken "Java Concurrency in Practice". Även om den släpptes 2006, säger folk att boken är ganska grundläggande och fortfarande relevant idag. Du kan till exempel läsa diskussionen här: Är "Java Concurrency In Practice" fortfarande giltig? . Det är också bra att läsa länkarna i diskussionen. Till exempel finns det en länk till boken The Well-Grounded Java Developer , och vi ska särskilt nämna kapitel 4. Modern concurrency . Det finns också en hel recension om detta ämne:Är "Java Concurrency in Practice" fortfarande giltigt i Java 8:s era? Den artikeln ger också förslag om vad mer du kan läsa för att verkligen förstå detta ämne. Efter det kan du ta en titt på en bra bok som OCA/OCP Java SE 8 Programmer Practice Tests . Vi är intresserade av den andra akronymen: OCP (Oracle Certified Professional). Du hittar tester i "Kapitel 20: Java Concurrency". Den här boken har både frågor och svar med förklaringar. Till exempel: Bättre tillsammans: Java och trådklassen.  Del VI — Skjut loss!  - 3Många kanske börjar säga att den här frågan är ännu ett exempel på memorering av metoder. Å ena sidan, ja. Å andra sidan kan du svara på den här frågan genom att komma ihåg att det ExecutorServiceär en slags "uppgradering" av Executor. OchExecutorär avsett att helt enkelt dölja hur trådar skapas, men det är inte det huvudsakliga sättet att köra dem, det vill säga starta ett Runnableobjekt på en ny tråd. Det är därför det inte finns någon execute(Callable)— för i ExecutorService, lägger den Executorhelt enkelt till submit()metoder som kan returnera ett Futureobjekt. Naturligtvis kan vi memorera en lista med metoder, men det är mycket lättare att göra vårt svar baserat på vår kunskap om själva klassernas karaktär. Och här är ytterligare material om ämnet: Bättre tillsammans: Java och trådklassen. Del I — Trådar av utförande Bättre tillsammans: Java och klassen Thread. Del II — Synkronisering Bättre tillsammans: Java och klassen Thread. Del III — Interaktion Bättre tillsammans: Java och klassen Thread. Del IV — Callable, Future och friends Bättre tillsammans: Java och Thread-klassen. Del V — Executor, ThreadPool, Fork/Join
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION