[JAVA] Punkt 81: Bevorzugen Sie Parallelitätsdienstprogramme, um zu warten und zu benachrichtigen
81 Wählen Sie unter Warten und Benachrichtigen das Dienstprogramm Parallelität aus
- Seit Java5 kann die Verarbeitung, die zuvor durch Warten und Benachrichtigen durchgeführt wurde, jetzt mit einem Dienstprogramm für Parallelität auf hoher Ebene durchgeführt werden. Diese sollten stattdessen verwendet werden.
- Hochrangige Parallelitätsdienstprogramme können in drei Teile unterteilt werden.
- Executor Framework (Item80)
- gleichzeitige Sammlung. Dies ist diejenige, die in List, Map und Queue implementiert ist, damit die parallele Verarbeitung gut durchgeführt werden kann. Diese sind intern synchronisiert, sodass das Sperren den Prozess einfach verlangsamt. Dieser Teil eignet sich für zustandsabhängige Änderungsvorgänge. Mit dem Aufkommen gleichzeitiger Sammlungen sind synchronisierte Sammlungen zu einer Zahlungsbox geworden.
- Synchronisierer. Synchronizer ermöglichen Bewegungen, die von anderen hervorgehoben werden, z. B. das Warten auf den Abschluss der Verarbeitung anderer Threads. Berühmt sind CountDownLatch, Semaphore, CyclicBarrier, Exchanger, Phaser und mehr.
- Code mit CountDownLatch sieht folgendermaßen aus: Um die Zeitmethode zu erklären, wird, wenn ready dreimal countDown ist, ready await gelöst, startNanos gemessen, start countDown einmal ausgeführt, die Aktion ausgeführt und jeder Thread countDown ausgeführt. Wenn dann alle drei heruntergezählt sind, ist das Warten beendet und die benötigte Zeit wird zurückgegeben. Diese Methode wird nicht beendet, wenn die Anzahl der Threads geringer ist als die im zweiten Argument angegebene Anzahl (Thread-Hunger-Deadlock).
package tryAny.effectiveJava;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
Executor e = Executors.newFixedThreadPool(3); //Wenn das Argument hier kleiner als der Wert des zweiten Zeitarguments ist, wird der Prozess nicht beendet.
System.out.println(time(e, 3, () -> System.out.println("a")));
}
// Simple framework for timing concurrent execution
public static long time(Executor executor, int concurrency, Runnable action) throws InterruptedException {
CountDownLatch ready = new CountDownLatch(concurrency);
CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(concurrency);
for (int i = 0; i < concurrency; i++) {
executor.execute(() -> {
ready.countDown(); // Tell timer we're ready
try {
start.await(); // Wait till peers are ready
action.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown(); // Tell timer we're done
}
});
}
ready.await(); // Wait for all workers to be ready
long startNanos = System.nanoTime();
start.countDown(); // And they're off!
done.await(); // Wait for all workers to finish
return System.nanoTime() - startNanos;
}
}
- Im neu geschriebenen Code wird das Warten und Benachrichtigen nicht mehr angezeigt. Sie können es in der Wartung von Legacy-Code sehen.
- Wenn Sie nur eine Zusammenfassung über Warten und Benachrichtigen schreiben,
- Verwenden Sie wait in a while-Schleife.
- Sie sollten notifyAll verwenden, anstatt notify. Wenn Sie notify verwenden, müssen Sie auf das Überleben des Threads achten.