JAVA-B4
Parallel Computing (Parallelism)
Parallelism
Multithreading
Klassische Threads
Threads & Runnables
Overview
Thread (Begriff)
DruckThread
DruckRunnable
ThreadDemo
Output
Thread Lifecycle
Overview
start()
run()
join()
stop()?
StartRunJoinDemo Example
Von allein endende Threads
Threads mit while(criteria)
Daemon-Threads
Overview
ThreadDaemonDemo
Daemon (Summary)
Thread-Priority
Overview
t.setPriority(..)
MachineThread Klasse (Example)
ThreadPriorityDemo (Example)
Thread Notification
Grundlagen
wait
notify
notifyAll
sleep
sleep vs. wait
Thread Notification Demo
sychronized(monitor)
Notification/Monitor Zusammenfassung
Concurrent API
Vom Thread zur Concurrent API
Was ist Concurrency?
Was ist Concurrency?
Problemstellungen bei herkömmlichen Threads
Features der Concurrent API
Warum: Bedeutung des Umstiegs
Themenüberblick
Technischer Hintergrund
Thread.sleep(0, 500_000)
System.nanoTime()
Executors (Übersicht) #310
ExecutorService (Page)
Begriffsbestimmung
Excetutor
.execute()
ExecutorService (Begriff)
ExecutorService Interface Methoden
Mehrere Callables und Runnables auf einmal starten
Kontrolliertes Beenden
Executors-Utilily Klasse
newCachedThreadPool()
newSingleThreadExecutor()
newFixedThreadPool(int nThreads)
newWorkStealingPool()
newScheduledThreadPool(int corePoolSize)
Beispiel
ForkJoinPool, RecursiveTask, Executors Example#09
A | B1,B2,B3 | C mit Summenbildung.
Executor vs. ExecutorService Example#10
Executors.newFixedThreadPool(2)
Beispiel
Zusammenfassung Executors
Callable, Future #330
Callable Future (Begriff)
Callable Interface (Begriff)
Begriff
Callable erzeugen
Callable verwenden
Future
Beispiel
Future-Listen mit invokeAll
Was sind Future-Listen?
Wie erzeuge ich Future-Listen?
Wann blockt eine Future-Liste?
Beispiel mit 12 Callables in 4 Gruppen
Executors (#350) @! Bis auf Hinweis doppelt
Overview
Executor (Begriff)
ExecutorService (Begriff)
Executors.new~ Factory Methoden
Executors für ThreadPoolExecutor mit fixed Pool
ScheduledExecutorService
ScheduledExecutorService
Superklasse ExecutorService
schedule(*)
scheduleAtFixedRate(*)
scheduleWithFixedDelay(*)
ScheduledExecutorService (xmpl)
ProductClock(Main, Bsp.)
AbstractMachine**
AbstractMaterial**
Coal/Iron/Steel**
Coal-/Iron-/Steel-Machine**
Material**
ProductionBus-Machine**
Storage T**
ThreadPoolExecutor (Detail)
Executor Implementierung
ThreadPoolExecutor (Term)
ThreadPoolExecutor Beispiele
"FixedSizeExecutorExample" EXAMPLE C20
"CachedExecutorExample" EXAMPLE C21
SynchronousQueue
"ScheduledExecutorExample" EXAMPLE C22
"CustomSingleThreadExecutorRunnableComponent" EXAMPLE C23
"CustomPolicyRejectedExecutorExample" EXAMPLE C24
"PrioritizedCombinerRunnableComponent" EXAMPLE C30
"BufferingRejectedTasksExample" EXAMPLE C31
Java RejectedExecutionHandler
ThreadPoolExecutor FAQ
Synchronizer
Synchronizer (overview)
Synchronizer Begriff
CountDownLatch
CountDownLatch Example
CyclicBarrier
CyclicBarrier Example (@Zeit ausgeben!)
Phaser
Phaser Example(@Erl!)
Exchanger
Exchanger ping/pong
Semaphore
CompletableFuture
async!!!
SemaphoreCompletableFutureDemo (@!?)
Monitoring
JMX
ThreadPoolExecutor "live" überwachen
getActiveCount()
getPoolSize()
getCompletedTaskCount()
getQueue().size()
Concurrency
Example:DeterministicCopy @todo FILE LINK FEHLT!
Example:NonDeterministicCopy @todo FILE LINK FEHLT!
Kapitel java.util.concurrent.Flow @!fehltReactive Flow @!fehltPublisher TSubscriber TSubscription TProcressor TProcressor T
Synchronizer
Concurrent Synchronizer | Begriffsbestimmung
Ein Concurrent Synchronizerl
java.util.concurrent beinhaltet eine ganze Reihe von Synchronizern. All diese Synchrozier sind im Kern darauf spezialisiert, die Zusammenarbeit oder auch die Abstimmung zwischen verschiedenen Threads zu ermöglichen.
Man bezeichnet das auch als "Concurrency Coordination" oder Koordination im Bereich der nebenläufigen Programmierung.
Synchronizer-Kategorie
├─ CountDownLatch Countdown für mehrere Threads
├─ CyclicBarrier Barriere für Gruppen-Threads
├─ Phaser Phasenbasierte Koordination
├─ Exchanger Daten austauschen zwischen zwei Threads
└─ Semaphore Begrenzte Zugriffsanzahl
Abbildung: Diagramm mit Übersicht von Synchronizern aus java.util.concurrent.
(ContentInstance snewmedia_java_concurrent_synchronizers)
Warten auf mehrere Events: CountDownLatch.
Gruppensynchronisation: CyclicBarrier. Es gibt nur eine Barriere und eine fixe Anzahl von Threads.
Mehrere Phasen, flexibel: Phaser.
Gegenseitiger Austausch: Exchanger mit Typparameter T
Ressourcenzugriff regeln: Semaphore. Ermöglich es, dass für den Pool, welchen die Semaphore verwaltet, z. B. nur 2 Threads gleichzeitig arbeiten dürfen.
CountDownLatch
Synchronizers | CountDownLatch
Der CountDownLatch Synchronizer erfasst alle Tasks und führt Sie in einer beliebigen Reihe aus. Mit Hilfe von latch.await() wird gewartet, bis alle Tasks fertig sind.
Code
package com.stuelken.java.b4.parallel.c40.synchronizer;
import java.util.concurrent.CountDownLatch;
/**
* @author t2m
*/
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
new CountDownLatchExample().doCountDownLatch();
}
/**
* Use Case: Warten auf mehrere Vorbereitungen (z. B. Game-Assets,
* Spieler-Eingaben)
* @throws InterruptedException
*/
public void doCountDownLatch() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable worker = () -> {
System.out.println("Worker "+ Thread.currentThread().getName()+" gestartet");
latch.countDown();
};
for (int i = 0; i < 10; i++) {
new Thread(worker, "T "+i).start();
}
latch.await(); // blockiert bis alle Threads durch sind
System.out.println("Alle Worker fertig");
}
}
Ausgabe
// @formatter:off
/*
Worker T 0 gestartet
Worker T 3 gestartet
Worker T 2 gestartet
Worker T 1 gestartet
Alle Worker fertig
Worker T 5 gestartet
Worker T 6 gestartet
Worker T 7 gestartet
Worker T 8 gestartet
Worker T 4 gestartet
Worker T 9 gestartet
*/
Synchronizers | CyclicBarrier
C
Mit Hilfe des CyclicBarrier Synchronizers simulieren wir in diesem Beispiel ein Wettrennen. Alle Player-Runnables warten auf "Auf die Plätze", dann "Fertig" und "Los!".
In der Phase des Rennens wird mit Zufallszahlen Task für je 10 Meter im Rennen eine andere Laufzeit mitgegeben.
Code
package com.stuelken.java.b4.parallel.c40.synchronizer;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author t2m
*/
public class CyclingBarrierExample {
public static void main(String[] args) {
doCyclingBarrier();
}
/**
* Use Case: Alle Spieler müssen erst fertig sein, bevor die Runde fortschreitet
*/
public static void doCyclingBarrier() {
int parties = 3;
// Die Aktion, welche die CyclingBarrier ausführt.
Runnable barrierAction_A = () -> {
System.out.println("Auf die Plaetze!");
};
Runnable barrierAction_B = () -> {
System.out.println("Fertig ... ");
};
Runnable barrierAction_C = () -> {
System.out.println("Los!!!");
};
// Instanz erzeugten mit Anzahl der Akteure und der Aktion.
CyclicBarrier barrierA = new CyclicBarrier(parties, barrierAction_A);
CyclicBarrier barrierB = new CyclicBarrier(parties, barrierAction_B);
CyclicBarrier barrierC = new CyclicBarrier(parties, barrierAction_C);
// Jeder Spieler wird statt als Klasse kurzerhand als
// Runnable einzig und allein mit einer Funktion
// gestartet.
Runnable player = () -> {
String i = Thread.currentThread().getName();
try {
System.out.println("Spieler " + i + " wartet auf A");
barrierA.await();
System.out.println("Spieler " + i + " wartet auf B");
barrierB.await();
System.out.println("Spieler " + i + " wartet auf C");
barrierC.await();
Random r = new Random();
for (int j = 0; j < 100; j = j + 10) {
System.out.println("Spieler " + i + " rennt und erreicht " + j + "m");
Thread.sleep(r.nextInt(1000) + 2000);
}
System.out.println("Spieler " + i + " kommt ins Ziel!");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
} // blockiert
System.out.println("Spieler in Thread " + i + " jubelt!");
};
/*
* 3 Threads für je einen Player-Runnable.
*/
for (int i = 0; i < 3; i++) {
Thread t = new Thread(player);
t.setName("T" + i);
t.start();
}
/*
* @todo: Berechne als Ergebnis des Programms, wer das Rennen am Ende gewonnen
* hat, indem nur Klassen/Interfaces aus java.concurrent statt Threads genutzt
* werden.
*/
/*
* @todo: Gebe aus, wer gerade im Verlauf des Rennens die Führung hat.
*/
}
}
Ausgabe
// @formatter:off
/*
Spieler T1 wartet auf A
Spieler T2 wartet auf A
Spieler T0 wartet auf A
Auf die Plaetze!
Spieler T0 wartet auf B
Spieler T1 wartet auf B
Spieler T2 wartet auf B
Fertig ...
Spieler T2 wartet auf C
Spieler T1 wartet auf C
Spieler T0 wartet auf C
Los!!!
Spieler T0 rennt und erreicht 0m
Spieler T2 rennt und erreicht 0m
Spieler T1 rennt und erreicht 0m
Spieler T0 rennt und erreicht 10m
Spieler T2 rennt und erreicht 10m
Spieler T1 rennt und erreicht 10m
Spieler T1 rennt und erreicht 20m
Spieler T0 rennt und erreicht 20m
Spieler T2 rennt und erreicht 20m
Spieler T1 rennt und erreicht 30m
Spieler T0 rennt und erreicht 30m
Spieler T2 rennt und erreicht 30m
Spieler T1 rennt und erreicht 40m
Spieler T0 rennt und erreicht 40m
Spieler T2 rennt und erreicht 40m
Spieler T1 rennt und erreicht 50m
Spieler T0 rennt und erreicht 50m
Spieler T2 rennt und erreicht 50m
Spieler T1 rennt und erreicht 60m
Spieler T0 rennt und erreicht 60m
Spieler T2 rennt und erreicht 60m
Spieler T0 rennt und erreicht 70m
Spieler T1 rennt und erreicht 70m
Spieler T2 rennt und erreicht 70m
Spieler T0 rennt und erreicht 80m
Spieler T1 rennt und erreicht 80m
Spieler T2 rennt und erreicht 80m
Spieler T1 rennt und erreicht 90m
Spieler T0 rennt und erreicht 90m
Spieler T2 rennt und erreicht 90m
Spieler T1 kommt ins Ziel!
Spieler in Thread T1 jubelt!
Spieler T0 kommt ins Ziel!
Spieler in Thread T0 jubelt!
Spieler T2 kommt ins Ziel!
Spieler in Thread T2 jubelt!
*/
Synchronizers | Phaser
Phaser
Ein Phaser-Synchronizer erstellt Phasen.
Code
package com.stuelken.java.b4.parallel.c40.synchronizer;
import java.util.concurrent.*;
public class PhaserHelper {
public void doPhaser() throws InterruptedException, ExecutionException {
int parties = 1;
Phaser phaser = new Phaser(parties); // Hauptthread registriert
Callable<String> phaseTask = () -> {
phaser.register();
for (int i = 1; i <= 4; i++) {
System.out.println(Thread.currentThread().getName() + " Phase " + i + " gestartet");
phaser.arriveAndAwaitAdvance(); // auf nächste Phase warten
}
return Thread.currentThread().getName() + " ist fertig.";
};
ExecutorService pool = Executors.newFixedThreadPool(3);
Future<String> f1 = pool.submit(phaseTask);
Future<String> f2 = pool.submit(phaseTask);
Future<String> f3 = pool.submit(phaseTask);
// Hauptthread nimmt ebenfalls an 4 Phasen teil:
for (int i = 1; i <= 4; i++) {
System.out.println("Main thread Phase " + i + " beendet");
phaser.arriveAndAwaitAdvance();
}
// Ausgabe von Ergebnissen (damit main bis zum Ende wartet):
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
pool.shutdown();
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
new PhaserHelper().doPhaser();
}
}
Ausgabe
// @formatter:off
/*
Main thread Phase 1 beendet
pool-1-thread-1 Phase 1 gestartet
pool-1-thread-2 Phase 1 gestartet
pool-1-thread-3 Phase 1 gestartet
pool-1-thread-3 Phase 2 gestartet
Main thread Phase 2 beendet
pool-1-thread-1 Phase 2 gestartet
pool-1-thread-2 Phase 2 gestartet
pool-1-thread-2 Phase 3 gestartet
Main thread Phase 3 beendet
pool-1-thread-3 Phase 3 gestartet
pool-1-thread-1 Phase 3 gestartet
pool-1-thread-1 Phase 4 gestartet
pool-1-thread-2 Phase 4 gestartet
pool-1-thread-3 Phase 4 gestartet
Main thread Phase 4 beendet
pool-1-thread-1 ist fertig.
pool-1-thread-2 ist fertig.
pool-1-thread-3 ist fertig.
*/
Exchanger
Synchronizers | Exchanger
Der Exchanger Synchronizer ermöglich es, dass ein Task in seinem Verlauf sich an einen Exchanger wendet, um mit exchange(..) einen Wert anzubieten und darauf zuwarten, dass ein anderer Thread auch einen Wert bietet, so dass die zwei Werte tauschen.
Dieses Verfahren ist an dem Punkt praktisch, dass die zwei Tasks unabhängig voneinander programmiert werden können und einzig und allein den Exchanger adressieren müssen.
Code
package com.stuelken.java.b4.parallel.c40.synchronizer;
import java.util.concurrent.Exchanger;
/**
* @author t2m
*/
public class ExchangerExample {
public static void main(String[] args) {
new ExchangerExample().doExchanger();
}
/**
*
*/
public void doExchanger() {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
// Wartet auf einen anderen Thread.
String response = exchanger.exchange("Ping from A");
System.out.println(Thread.currentThread().getName()+" Erhielt: " + response);
} catch (InterruptedException e) {
}
},"A").start();
new Thread(() -> {
try {
// Wartet auf einen anderen Thread.
String response = exchanger.exchange("Pong from B");
System.out.println(Thread.currentThread().getName()+" Erhielt: " + response);
} catch (InterruptedException e) {
}
},"B").start();
}
}
Ausgabe
// B Erhielt: Ping from A
// A Erhielt: Pong from B
Synchronizers | Semaphore
C
Das SemaphoreHelper Beispiele für die Semaphore zeigt, wie man den Zugriff auf jeweils 2 Tasks zur Zeit beschränken kann. Die Reihenfolge wird entweder unfair oder fair ermittelt: Bei fair stimmt die Reihenfolge mit der Registrierung überein.
Gesteuert wird der Ablauf über einen Executor in Verbindung mit einem CountDownLatch, damit zuerst der erste Teil mit unfair erfolgt und danach der zweite Teil mir fair.
Code
package com.stuelken.java.b4.parallel.c40.synchronizer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* Semaphore dienen als Synchronizer für Rate Limiting, begrenzter
* Ressourcenzugriff.
*
*
* @author t2m
*
*/
public class SemaphoreHelper {
public static class MySemaphore {
}
public static void main(String[] args) {
runExample();
}
/**
*
*/
public static void runExample() {
ExecutorService executor = Executors.newFixedThreadPool(10);
// FALSCH!!!!
// CountDownLatch latch = new CountDownLatch(10);
// Jeder Latch wird nur 1x ausgeführt! Wir wollen
// aber zwei Gruppen.
CountDownLatch latchUnfair = new CountDownLatch(10);
CountDownLatch latchFair = new CountDownLatch(10);
/**
* Creates a Semaphore with the given number of permits and nonfair fairness
* setting. Parameters:permits the initial number of permits available.This
* value may be negative, in which case releasesmust occur before any acquires
* will be granted.
*/
Semaphore semaphoreNotFair = new Semaphore(2, false);
Semaphore semaphoreFair = new Semaphore(2, true);
/**
*
*/
Runnable accessUnfair = () -> {
try {
/*
* {#1} Statt eines Monitor-Objekts wird ein Semaphore-Synchronizer verwendet.
*
* Acquires a permit from this semaphore, blocking until one is available.
*
* <p>Acquires a permit, if one is available and returns immediately, reducing
* the number of available permits by one.
*
* <p>If no permit is available then the current thread becomes disabled for
* thread scheduling purposes and lies dormant until some other thread invokes
* the {@link #release} method for this semaphore and the current thread is next
* to be assigned a permit.
*
* <p>If the current thread is {@linkplain Thread#interrupt interrupted} while
* waiting for a permit then it will continue to wait, but the time at which the
* thread is assigned a permit may change compared to the time it would have
* received the permit had no interruption occurred. When the thread does return
* from this method its interrupt status will be set.
*/
System.out.println(Thread.currentThread().getName() + " wartet...");
semaphoreNotFair.acquire(); // WICHTIG!
System.out.println(Thread.currentThread().getName() + " darf rein");
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
/*
* Releases a permit, returning it to the semaphore.
*
* <p>Releases a permit, increasing the number of available permits by one. If
* any threads are trying to acquire a permit, then one is selected and given
* the permit that was just released. That thread is (re)enabled for thread
* scheduling purposes.
*
* <p>There is no requirement that a thread that releases a permit must have
* acquired that permit by calling {@link #acquire}. Correct usage of a
* semaphore is established by programming convention in the application.
*/
System.out.println(Thread.currentThread().getName() + " released die Semaphore.");
semaphoreNotFair.release(); // WICHTIG!
// Wenn wir einen Latch verwenden, müssen wir dafür sorgen, dass
// der Latch auch etwas zu zählen hat. Anderenfalls weiß
// Latch nicht, wann alle Tasks abgearbeitet sind, und latch.await()
// endet nicht.
latchUnfair.countDown(); // WICHTIG!
}
};
/**
* === Definition eines Runnables für "fair".
*/
Runnable accessFair = () -> {
try {
System.out.println(Thread.currentThread().getName() + " wartet...");
semaphoreFair.acquire(); // WICHTIG!
System.out.println(Thread.currentThread().getName() + " darf rein");
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
System.out.println(Thread.currentThread().getName() + " released die Semaphore.");
semaphoreFair.release(); // WICHTIG! Nicht Fair/Unfair verwechseln!
// Wenn wir einen Latch verwenden, müssen wir dafür sorgen, dass
// der Latch auch etwas zu zählen hat. Anderenfalls weiß
// Latch nicht, wann alle Tasks abgearbeitet sind, und latch.await()
// endet nicht.
latchFair.countDown(); // WICHTIG!
}
};
System.out.println("Unfair:");
for (int i = 0; i < 10; i++) {
new Thread(accessUnfair).start();
}
try {
latchUnfair.await();
System.out.println("\nUNFAIR-Fertig, jetzt FAIR...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println();
System.out.println("Fair:");
for (int i = 0; i < 10; i++) {
new Thread(accessFair).start();
}
try {
latchFair.await();
System.out.println("\nFAIR-Fertig.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Beenden.
executor.shutdown();
}
}
Ausgabe
// @formatter:off
/*
Unfair:
Thread-0 wartet...
Thread-0 darf rein
Thread-1 wartet...
Thread-1 darf rein
Thread-2 wartet...
Thread-3 wartet...
Thread-4 wartet...
Thread-5 wartet...
Thread-7 wartet...
Thread-6 wartet...
Thread-8 wartet...
Thread-9 wartet...
Thread-1 released die Semaphore.
Thread-0 released die Semaphore.
Thread-3 darf rein
Thread-2 darf rein
Thread-3 released die Semaphore.
Thread-2 released die Semaphore.
Thread-4 darf rein
Thread-5 darf rein
Thread-4 released die Semaphore.
Thread-7 darf rein
Thread-5 released die Semaphore.
Thread-6 darf rein
Thread-7 released die Semaphore.
Thread-8 darf rein
Thread-6 released die Semaphore.
Thread-9 darf rein
Thread-8 released die Semaphore.
Thread-9 released die Semaphore.
UNFAIR-Fertig, jetzt FAIR...
Fair:
Thread-11 wartet...
Thread-11 darf rein
Thread-10 wartet...
Thread-10 darf rein
Thread-12 wartet...
Thread-13 wartet...
Thread-14 wartet...
Thread-15 wartet...
Thread-16 wartet...
Thread-18 wartet...
Thread-17 wartet...
Thread-19 wartet...
Thread-11 released die Semaphore.
Thread-10 released die Semaphore.
Thread-12 darf rein
Thread-14 darf rein
Thread-12 released die Semaphore.
Thread-13 darf rein
Thread-14 released die Semaphore.
Thread-15 darf rein
Thread-13 released die Semaphore.
Thread-16 darf rein
Thread-15 released die Semaphore.
Thread-17 darf rein
Thread-17 released die Semaphore.
Thread-16 released die Semaphore.
Thread-18 darf rein
Thread-19 darf rein
Thread-18 released die Semaphore.
Thread-19 released die Semaphore.
FAIR-Fertig.
*/
Synchronizers | SemaphoreCompletableFutureDemo
C
Dieses Beispiel zeigt, wie man die zwei Varianten
mit der fairen und der unfairen Semaphore getrennt
nacheinander starten kann. Jede Semaphore lässt nur
2 Task auf einmal zu. CompleteableFuture
ist der Ansatz, wie man in Java auch asynchron programmieren
kann.
data
Code
package com.stuelken.java.b4.parallel.c40.synchronizer;
import java.util.concurrent.*;
import java.util.*;
import java.util.stream.*;
/**
*
* @author t2m
*/
public class SemaphoreCompletableFutureDemo {
public static void main(String[] args) {
Semaphore semaphoreUnfair = new Semaphore(2, false);
Semaphore semaphoreFair = new Semaphore(2, true);
ExecutorService executor = Executors.newFixedThreadPool(10);
// Erst unfair laufen lassen
List<CompletableFuture<Void>> unfairTasks = IntStream.range(0, 10)
.mapToObj(
i -> CompletableFuture.runAsync(() -> task("Unfair-" + i, semaphoreUnfair), executor))
.collect(Collectors.toList());
// Wenn alle unfair fertig → dann fair starten
CompletableFuture
.allOf(unfairTasks.toArray(new CompletableFuture[0]))
.thenRun(() -> {
System.out.println("\nAlle Unfair-Tasks fertig. Starte Fair-Phase...\n");
List<CompletableFuture<Void>> fairTasks = IntStream.range(0, 10)
.mapToObj(
i -> CompletableFuture.runAsync(() -> task("Fair-" + i, semaphoreFair), executor))
.collect(Collectors.toList());
CompletableFuture
.allOf(fairTasks.toArray(new CompletableFuture[0]))
.thenRun(() -> {
System.out.println("\nAlle Fair-Tasks abgeschlossen.");
executor.shutdown();
});
});
}
static void task(String name, Semaphore semaphore) {
try {
System.out.println(name + " wartet...");
semaphore.acquire();
System.out.println(name + " hat Zugriff");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
System.out.println(name + " gibt Zugriff frei");
}
}
}
Ausgabe
// @formatter:off
/*
Unfair-0 wartet...
Unfair-9 wartet...
Unfair-8 wartet...
Unfair-7 wartet...
Unfair-5 wartet...
Unfair-6 wartet...
Unfair-4 wartet...
Unfair-3 wartet...
Unfair-2 wartet...
Unfair-1 wartet...
Unfair-9 hat Zugriff
Unfair-0 hat Zugriff
Unfair-4 hat Zugriff
Unfair-9 gibt Zugriff frei
Unfair-0 gibt Zugriff frei
Unfair-3 hat Zugriff
Unfair-4 gibt Zugriff frei
Unfair-5 hat Zugriff
Unfair-3 gibt Zugriff frei
Unfair-7 hat Zugriff
Unfair-7 gibt Zugriff frei
Unfair-5 gibt Zugriff frei
Unfair-6 hat Zugriff
Unfair-8 hat Zugriff
Unfair-2 hat Zugriff
Unfair-6 gibt Zugriff frei
Unfair-8 gibt Zugriff frei
Unfair-1 hat Zugriff
Unfair-2 gibt Zugriff frei
Unfair-1 gibt Zugriff frei
Alle Unfair-Tasks fertig. Starte Fair-Phase...
Fair-0 wartet...
Fair-0 hat Zugriff
Fair-2 wartet...
Fair-2 hat Zugriff
Fair-1 wartet...
Fair-4 wartet...
Fair-3 wartet...
Fair-7 wartet...
Fair-6 wartet...
Fair-5 wartet...
Fair-8 wartet...
Fair-9 wartet...
Fair-0 gibt Zugriff frei
Fair-1 hat Zugriff
Fair-4 hat Zugriff
Fair-2 gibt Zugriff frei
Fair-3 hat Zugriff
Fair-7 hat Zugriff
Fair-1 gibt Zugriff frei
Fair-4 gibt Zugriff frei
Fair-7 gibt Zugriff frei
Fair-5 hat Zugriff
Fair-6 hat Zugriff
Fair-3 gibt Zugriff frei
Fair-5 gibt Zugriff frei
Fair-8 hat Zugriff
Fair-6 gibt Zugriff frei
Fair-9 hat Zugriff
Fair-8 gibt Zugriff frei
Fair-9 gibt Zugriff frei
Alle Fair-Tasks abgeschlossen.
*/
UIO3 Es ist einfacher als Du denkst.
Stelle noch heute Deine Anfrage.

