JavaRush /Java-Blog /Random-DE /Man kann Java nicht mit einem Thread verderben: Teil VI –...
Viacheslav
Level 3

Man kann Java nicht mit einem Thread verderben: Teil VI – Zur Barriere!

Veröffentlicht in der Gruppe Random-DE

Einführung

Streams sind eine interessante Sache. In früheren Rezensionen haben wir uns einige der verfügbaren Tools zur Implementierung von Multithreading angesehen. Mal sehen, was wir sonst noch für interessante Dinge tun können. An diesem Punkt wissen wir viel. Aus „ Man kann Java nicht mit einem Thread verderben: Teil I – Threads “ wissen wir beispielsweise , dass ein Thread ein Thread ist. Wir wissen, dass ein Thread eine Aufgabe ausführt. Wenn wir möchten, dass unsere Aufgabe ausgeführt werden kann ( run), müssen wir den Thread als einen bestimmten Thread angeben Runnable. Man kann Java nicht mit einem Thread verderben: Teil VI – Zur Barriere!  - 1Zur Erinnerung können wir den Tutorialspoint Java Online Compiler verwenden :
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();
}
Wir wissen auch, dass wir ein Konzept wie Schloss haben. Wir haben darüber in „ You Can’t Spoil Java with a Thread: Part II – Synchronization “ gelesen. Ein Thread kann eine Sperre belegen, und dann muss ein anderer Thread, der versucht, die Sperre zu besetzen, warten, bis die Sperre freigegeben wird:
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();
	}
}
Ich denke, es ist an der Zeit, darüber zu sprechen, was wir sonst noch tun können, was interessant ist.

Semaphore

Das einfachste Mittel zur Steuerung, wie viele Threads gleichzeitig arbeiten können, ist ein Semaphor. Wie bei der Eisenbahn. Das grüne Licht ist an – Sie können. Das rote Licht leuchtet – wir warten. Was erwarten wir von einem Semaphor? Berechtigungen. Erlaubnis auf Englisch - Erlaubnis. Um die Erlaubnis zu erhalten, müssen Sie diese einholen, die auf Englisch erhältlich ist. Und wenn die Erlaubnis nicht mehr benötigt wird, müssen wir sie weggeben, das heißt, sie freigeben oder loswerden, was auf Englisch „Release“ heißt. Mal sehen, wie es funktioniert. Wir müssen die Klasse importieren java.util.concurrent.Semaphore. Beispiel:
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);
}
Wie wir sehen können, verstehen wir, nachdem wir englische Wörter auswendig gelernt haben, wie Semaphor funktioniert. Interessanterweise besteht die Hauptbedingung darin, dass das Semaphor-„Konto“ über eine positive Anzahl von Genehmigungen verfügen muss. Daher können Sie es mit einem Minus einleiten. Und Sie können mehr als 1 anfordern (erwerben).

CountDownLatch

Der nächste Mechanismus ist CountDownLatch. CountDown ist auf Englisch ein Countdown und Latch ist ein Riegel oder Riegel. Das heißt, wenn wir es übersetzen, dann ist dies ein Riegel mit Countdown. Hier benötigen wir den entsprechenden Import der Klasse java.util.concurrent.CountDownLatch. Es ist wie bei einem Rennen oder Rennen, bei dem sich alle an der Startlinie versammeln und wenn alle bereit sind, die Erlaubnis erteilt wird und alle gleichzeitig starten. Beispiel:
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();
 	}
}
Warten auf Englisch – erwarten. Das heißt, wir sprechen zuerst countDown. Wie Google Translator sagt, ist Countdown „ein Vorgang, bei dem Zahlen in umgekehrter Reihenfolge bis Null gezählt werden“, d. h. das Ausführen einer Countdown-Aktion, deren Zweck darin besteht, bis Null zu zählen. Und dann sagen wir await: Warten Sie, bis der Zählerwert Null wird. Interessant ist, dass eine solche Theke wegwerfbar ist. Wie es im JavaDoc heißt: „Wenn Threads wiederholt auf diese Weise heruntergezählt werden müssen, verwenden Sie stattdessen einen CyclicBarrier“, das heißt, wenn Sie einen wiederverwendbaren Zählwert benötigen, müssen Sie eine andere Option verwenden, die aufgerufen wird CyclicBarrier.

CyclicBarrier

Wie der Name schon sagt, CyclicBarrierhandelt es sich um eine zyklische Barriere. Wir müssen die Klasse importieren java.util.concurrent.CyclicBarrier. Schauen wir uns ein Beispiel an:
public static void main(String[] args) throws InterruptedException {
	Runnable action = () -> System.out.println("На старт!");
	CyclicBarrier berrier = new CyclicBarrier(3, action);
	Runnable task = () -> {
		try {
			berrier.await();
			System.out.println("Finished");
		} catch (BrokenBarrierException | InterruptedException e) {
			e.printStackTrace();
		}
	};
	System.out.println("Limit: " + berrier.getParties());
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
	}
}
Wie Sie sehen können, wird der Thread ausgeführt await, das heißt, er wartet. In diesem Fall sinkt der Wert der Barriere. Die Barriere gilt als durchbrochen ( berrier.isBroken()), wenn der Countdown Null erreicht. Um die Sperre zurückzusetzen, müssen Sie anrufen berrier.reset(), was in fehlte CountDownLatch.

Austauscher

Das nächste Mittel ist Exchanger. Austausch aus dem Englischen wird als Austausch oder Austausch übersetzt. A Exchangerist ein Austauscher, also etwas, durch das sie austauschen. Schauen wir uns ein einfaches Beispiel an:
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() + " обменялся с " + withThreadName);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	new Thread(task).start();
}
Hier starten wir zwei Threads. Jeder von ihnen führt die Austauschmethode aus und wartet darauf, dass ein anderer Thread ebenfalls die Austauschmethode ausführt. Somit tauschen die Threads die übergebenen Argumente untereinander aus. Interessante Sache. Erinnert sie dich an nichts? Und er erinnert daran SynchronousQueue, was das Herzstück von cachedThreadPool'a ist. Zur Verdeutlichung hier ein Beispiel:
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");
}
Das Beispiel zeigt, dass dieser Thread beim Starten eines neuen Threads in den Wartemodus wechselt, weil Die Warteschlange ist leer. Und dann mainstellt der Thread den Text „Nachricht“ in die Warteschlange. Gleichzeitig bleibt er für die erforderliche Zeit stehen, bis er dieses Textelement aus der Warteschlange erhält. Zu diesem Thema können Sie auch „ SynchronousQueue Vs Exchanger “ lesen.

Phaser

Und zum Schluss noch das Süßeste – Phaser. Wir müssen die Klasse importieren java.util.concurrent.Phaser. Schauen wir uns ein einfaches Beispiel an:
public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser();
        // Вызывая метод register, мы регистрируем текущий поток (main) Wie участника
        phaser.register();
        System.out.println("Phasecount is " + phaser.getPhase());
        testPhaser(phaser);
        testPhaser(phaser);
        testPhaser(phaser);
        // Через 3 секунды прибываем к барьеру и снимаемся регистрацию. Кол-во прибывших = кол-во регистраций = пуск
        Thread.sleep(3000);
        phaser.arriveAndDeregister();
        System.out.println("Phasecount is " + phaser.getPhase());
    }

    private static void testPhaser(final Phaser phaser) {
        // Говорим, что будет +1 участник на Phaser
        phaser.register();
        // Запускаем новый поток
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + " arrived");
            phaser.arriveAndAwaitAdvance(); //threads register arrival to the phaser.
            System.out.println(name + " after passing barrier");
        }).start();
    }
Das Beispiel zeigt, dass die Barriere bei Verwendung von Phaser'a durchbrochen wird, wenn die Anzahl der Registrierungen mit der Anzahl der Ankünfte an der Barriere übereinstimmt. Mehr erfahren Sie Phaserim Artikel vom Hub „ Neuer Phaser-Synchronisator “.

Ergebnisse

Wie Sie anhand der Beispiele sehen können, gibt es verschiedene Möglichkeiten, Threads zu synchronisieren. Vorhin habe ich versucht, mich an etwas zum Thema Multithreading zu erinnern. Ich hoffe, die vorherigen Teile waren hilfreich. Sie sagen, dass der Weg zum Multithreading mit dem Buch „Java Concurrency in Practice“ beginnt. Obwohl es bereits 2006 erschien, wird von den Leuten geantwortet, dass das Buch recht grundlegend ist und dennoch einiges zu bieten hat. Die Diskussionen können Sie zum Beispiel hier lesen: „ Ist Java Concurrency In Practice noch gültig? “. Es ist auch hilfreich, die Links aus der Diskussion zu lesen. Es gibt beispielsweise einen Link zum Buch „ The Well-Grounded Java Developer “, in dem es sich lohnt, auf „ Kapitel 4. Moderne Parallelität “ zu achten . Es gibt eine weitere ganze Rezension zum gleichen Thema: „ Ist Java Cocurrency in der Praxis im Zeitalter von Java 8 noch relevant ?“. Außerdem gibt es Tipps, was Sie sonst noch lesen sollten, um das Thema wirklich zu verstehen. Danach können Sie sich ein so wunderbares Buch wie „ OCA OCP JavaSE 8 Programmer Practice Tests “ genauer ansehen . Uns interessiert der zweite Teil, also OCP. Und es gibt Tests in „∫“. Dieses Buch enthält sowohl Fragen als auch Antworten mit Erklärungen. Zum Beispiel: Man kann Java nicht mit einem Thread verderben: Teil VI – Zur Barriere!  - 3Viele beginnen vielleicht zu sagen, dass dies nur ein weiteres Auswendiglernen von Methoden ist. Einerseits ja. Andererseits kann diese Frage beantwortet werden, indem man bedenkt, dass ExecutorServicees sich um eine Art „Upgrade“ handelt Executor. Und Executores soll lediglich die Methode zum Erstellen von Threads verbergen, nicht jedoch die Hauptmethode für deren Ausführung, d. h. das Ausführen eines neuen Threads Runnable. Deshalb execute(Callable)nein, weil Sie haben einfach Methoden hinzugefügt ExecutorService, die zurückkehren können . Wie Sie sehen, können wir uns eine Liste von Methoden merken, aber es ist viel einfacher, sie zu erraten, wenn wir die Natur der Klassen selbst kennen. Nun, einige zusätzliche Materialien zum Thema: ExecutorsubmitFuture #Wjatscheslaw
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION