CompletableFuture Detaillierte Erläuterung der gleichzeitigen JUC-Programmierung

Inhaltsverzeichnis

1. Zukünftige Schnittstelle

1.1 Einführung in die Zukunft

1.1.1 FutureTask

1.1.2 Codebeispiel

2. Abschließbare Zukunft

2.1 Grundkonzepte

 2.2 Codebeispiel

2.2.1 CompletableFuture erstellen

2.2.2 Funktionsschnittstelle (Ergänzung)

 2.2.3 Asynchrone Aufgabenzusammensetzung


1. Zukünftige Schnittstelle

1.1 Einführung in die Zukunft

Die Schnittstelle in der gleichzeitigen JUC-Programmierung Futureist ein in Java 5 eingeführter asynchroner Programmiermechanismus, der zur Darstellung eines Berechnungsergebnisses verwendet wird, das möglicherweise in Zukunft abgeschlossen wird. Es ermöglicht uns, eine Aufgabe zur Ausführung an den Thread-Pool oder andere Ausführer zu senden und Futuredas Ergebnis der Aufgabenausführung abzurufen oder über das Objekt zu beurteilen, ob die Aufgabe abgeschlossen wurde.

Die grundlegenden Methoden der Future-Schnittstelle sind wie folgt:

  1. isDone(): Stellen Sie fest, ob die Aufgabe abgeschlossen wurde.
  2. get(): Rufen Sie das Berechnungsergebnis der Aufgabe ab. Wenn die Aufgabe nicht abgeschlossen ist, wird der Aufruf dieser Methode blockiert, bis die Aufgabe abgeschlossen ist und das Ergebnis zurückgegeben wird.
  3. get(long timeout, TimeUnit unit): Rufen Sie das Berechnungsergebnis der Aufgabe ab, warten Sie jedoch höchstens die angegebene Zeit. Wenn die Aufgabe nicht innerhalb der angegebenen Zeit abgeschlossen wird, wird eine Timeout-Ausnahme ausgelöst.
  4. cancel(boolean mayInterruptIfRunning): Versuchen Sie, die Ausführung der Aufgabe abzubrechen. Wenn die Aufgabe bereits gestartet wurde, mayInterruptIfRunningbestimmt der Parameter, ob die Aufgabe unterbrochen werden soll.
  5. isCancelled(): Stellen Sie fest, ob die Aufgabe abgebrochen wurde.

1.1.1 FutureTask

FutureTaskIst eine Klasse in der gleichzeitigen Java-Programmierung, die Futuredie Schnittstelle implementiert und Runnabledie Implementierungsklasse ist. FutureTaskEs kann verwendet werden, um eine aufrufbare oder ausführbare Aufgabe zu umschließen, sodass wir die Aufgabe in einem anderen Thread ausführen und das Ergebnis der Aufgabe zu einem späteren Zeitpunkt erhalten können. FutureTaskStellt einige Methoden bereit, um das Ausführungsergebnis der Aufgabe zu erhalten und festzustellen, ob die Aufgabe abgeschlossen oder abgebrochen wurde.

Wie nachfolgend dargestellt:

 

 FutureTaskFutureDie Schnittstelle und die Schnittstelle sind implementiert Runnable, sodass Methoden von Futureund aufgerufen werden können.Runnable

1.1.2 Codebeispiel

import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(1);

        // 提交一个异步任务
        Future<String> future = executor.submit(() -> {
            try {
                Thread.sleep(2000); // 模拟耗时任务
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "异步任务执行完成";
        });

        System.out.println("异步任务已经提交");

        // 等待任务完成,并获取结果
        try {
            String result = future.get(); // 这里会阻塞,直到任务完成并返回结果
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // 关闭线程池
        executor.shutdown();
    }
}

Im obigen Beispiel erstellen wir ExecutorServiceeinen Thread-Pool mit einem Thread und submit()übergeben dann eine einfache asynchrone Aufgabe über die Methode, die während der Ausführung 2 Sekunden lang schläft und dann eine Zeichenfolge zurückgibt. Als nächstes verwenden wir Futuredie get()Methode, um das Ausführungsergebnis der Aufgabe abzurufen. Diese get()Methode blockiert, bis die Aufgabe abgeschlossen ist, und gibt das Ergebnis zurück.

Es ist zu beachten, dass Futuredie get()Methode beim Abrufen des Ergebnisses blockiert, was dazu führen kann, dass das Programm während des Wartevorgangs keine anderen Aufgaben ausführen kann. Um asynchrone Aufgaben besser bewältigen zu können, wurde in Java 8 eingeführt CompletableFuture, das flexiblere Methoden für die Handhabung asynchroner Aufgaben und Aufgabenkombinationen bietet und Futureeinige Einschränkungen bei der gleichzeitigen Programmierung besser lösen kann.

2. Abschließbare Zukunft

2.1 Grundkonzepte

CompletableFutureEs ist Teil des Java Concurrency Toolkit (Java Util Concurrent, JUC), das seit der Java 8-Version eingeführt wurde, um die Verarbeitung asynchroner Programmierung und gleichzeitiger Aufgaben zu vereinfachen. Es handelt sich um java.util.concurrenteine Klasse im Paket, die leistungsstarke Funktionen zum Umgang mit Abhängigkeiten zwischen asynchronen Vorgängen und Aufgaben bereitstellt.

CompletableFutureIst eine abschließbare oder programmierbare Zukunft. Dies kann manuell erfolgen oder zur Verkettung mehrerer asynchroner Vorgänge und zum Auslösen nachfolgender Vorgänge verwendet werden, wenn der Vorgang abgeschlossen ist. Es bietet eine Reihe von Methoden zum Kombinieren und Konvertieren asynchroner Aufgaben, z. B. thenApply(), , ,thenAccept()thenCombine()thenCompose()thenRun()等,CompletableFuture bietet einen Mechanismus ähnlich dem Beobachtermodus, der es ermöglicht, die hörende Partei nach Abschluss der Aufgabe zu benachrichtigen .

In Java8 bietet CompletableFuture eine sehr leistungsstarke Future-Erweiterungsfunktion, die uns dabei helfen kann, die Komplexität der asynchronen Programmierung zu vereinfachen, und bietet die Möglichkeit der funktionalen Programmierung, mit der Berechnungsergebnisse durch Rückrufe verarbeitet werden können, sowie Methoden zum Konvertieren und Kombinieren von CompletableFuture.
Es kann eine eindeutig abgeschlossene Zukunft oder eine Abschlussphase (CompletionStage) darstellen, die das Auslösen einiger Funktionen oder das Ausführen bestimmter Aktionen nach Abschluss der Berechnung unterstützt. Es implementiert die Schnittstellen Future und CompletionStage.


Wie nachfolgend dargestellt:

 2.2 Codebeispiel

2.2.1 CompletableFuture erstellen

CompletableFuture.runAsync(): Erstellen Sie ein CompletableFuture ohne Rückgabewert zum Ausführen asynchroner Aufgaben.

 ExecutorService threadPool = Executors.newFixedThreadPool(1);
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
        },threadPool);

CompletableFuture.supplyAsync(): Erstellt eine CompletableFuture mit einem Rückgabewert zum Durchführen asynchroner Berechnungen.

 ExecutorService threadPool = Executors.newFixedThreadPool(1);
  CompletableFuture<String> stringCompletableFuture = 
  CompletableFuture.supplyAsync(() -> {
            return "hello";
        }, threadPool);
        System.out.println(stringCompletableFuture.get());
        threadPool.shutdown();

CompletableFutureFuture的功能加强版,减少阻塞和轮询,可以传入回调对象,当异步任务完成后或发生异常时,自动调用回调对象的回调方法。代码如下:

  CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            return "hello";
        }).whenComplete((v,e)->{ //v表示返回的结果,e为异常,没有异常的时候e为null
            if(e==null){
                System.out.println("异步线程完成任务返回结果为 "+v);
            }
        }).exceptionally(e->{
            e.printStackTrace();
            System.out.println("goods");
            return null;
        });

Hier verwenden wir nicht unseren benutzerdefinierten Thread-Pool, sondern verwenden hier automatisch den Standard-Thread-Pool.

Hinweis: Wenn wir den Thread.sleep(1000)-Code zur Logikverarbeitung des asynchronen Threads hinzufügen und ihn erneut ausführen, werden wir die folgenden Ergebnisse finden:

 Da der Hauptthread die Verarbeitungsgeschwindigkeit des Benutzerthreads zu hoch ist,CompletableFuture默认使用的线程池会立刻关闭。所以推荐使用自定义线程池。

2.2.2 函数式接口(补充)

Da hier viele funktionale Schnittstellen zum Einsatz kommen, hier eine kurze Einführung.

Eine Java Functional Interface ist eine in der Java 8-Version eingeführte Funktion, bei der es sich um eine Schnittstelle handelt, die nur eine abstrakte Methode enthält. Funktionale Schnittstellen können als Schnittstellen betrachtet werden, die zur Unterstützung von Lambda-Ausdrücken verwendet werden, und Lambda-Ausdrücke können in Instanzen funktionaler Schnittstellen umgewandelt werden.

Die Java-Funktionsschnittstelle weist die folgenden Merkmale auf:

  1. Enthält nur eine abstrakte Methode: Eine funktionale Schnittstelle kann nur eine nicht implementierte abstrakte Methode haben. Es kann Standardmethoden und statische Methoden geben, aber nur eine abstrakte Methode.

  2. @FunctionalInterface- Annotation: Um sicherzustellen, dass eine Schnittstelle eine funktionale Schnittstelle ist, kann sie mit der @FunctionalInterface- Annotation markiert werden . Wenn die Schnittstelle nicht der Definition einer funktionalen Schnittstelle entspricht, gibt der Compiler eine Fehlermeldung aus.

Wie nachfolgend dargestellt:

 2.2.3 Asynchrone Aufgabenzusammensetzung

thenApply(): Konvertieren Sie das Berechnungsergebnis der vorherigen Stufe

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
    .thenApply(s -> s + " World");

thenAccept(): Verarbeiten Sie das Berechnungsergebnis der vorherigen Stufe, geben Sie das Ergebnis jedoch nicht zurück.

CompletableFuture.supplyAsync(() -> "Hello")
    .thenAccept(s -> System.out.println(s + " World"));

thenRun(): Führt die angegebene Aktion aus, nachdem die Aufgabe in der vorherigen Phase abgeschlossen ist, und hat keinen Rückgabewert.

   CompletableFuture<Void> hello = CompletableFuture.supplyAsync(() -> "Hello")
                .thenRun(() -> {
                    System.out.println("hello");
                });

thenCombine(): Kombiniert das Ergebnis zweier separater CompletableFutures.

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (s1, s2) -> s1 + s2);

handle(): Da die thenApply-Methode Abhängigkeiten erfordert, wird bei einem Fehler im aktuellen Schritt nicht mit dem nächsten Schritt fortgefahren, und das Handle kann dies tun

   CompletableFuture.supplyAsync(()->{
            System.out.println("执行第一步");
            Integer i=1/0;
            return 1;
        }).handle((v,e)->{
            System.out.println("执行第二步");
            return v+2;
        }).handle((v,e)->{
            System.out.println("执行第三步");
            return v+3;
        }).whenComplete((v,e)->{
            if (e == null) {
                System.out.println("最后结果:"+v);
            }
        }).exceptionally(e->{
            e.printStackTrace();
            return null;
        });

    }

applyToEither(): Ist CompletableFutureeine Methode in der Klasse, mit der zwei Ergebnisse verarbeitet werden CompletableFuture. Solange eines davon abgeschlossen ist, kann die angegebene Konvertierungsfunktion ausgeführt werden. applyToEitherMethoden werden in der asynchronen Programmierung häufig verwendet, wenn mehrere Aufgaben konkurrieren. Wir hoffen, dass die Ergebnisse verarbeitet werden können, ohne auf den Abschluss anderer Aufgaben warten zu müssen, solange eine der Aufgaben zuerst abgeschlossen wird.

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Future 1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Future 2";
        });

        CompletableFuture<String> resultFuture = future1.applyToEither(future2, s -> s + " wins!");

        resultFuture.thenAccept(System.out::println); // 输出较快的任务的结果

Zusammenfassung (Auszug aus Netzwerkdiagramm) 

Fügen Sie hier eine Bildbeschreibung ein

Notiz:

1. Es wird kein benutzerdefinierter Thread-Pool übergeben und der Standard-Thread-Pool ForkJoinPool wird verwendet.
2. Wenn Sie beim Ausführen der ersten Aufgabe einen benutzerdefinierten Thread-Pool übergeben:
Wenn Sie die thenRun-Methode aufrufen, um die zweite Aufgabe auszuführen, teilen sich die zweite Aufgabe und die erste Aufgabe denselben Thread-Pool.
Wenn Sie thenRunAsync aufrufen, um die zweite Aufgabe auszuführen, verwendet die erste Aufgabe den von Ihnen übergebenen Thread-Pool und die zweite Aufgabe den ForkJoinPool-Thread-Pool l Anmerkungen Es kann zu schnell verarbeitet werden, das System optimiert das Umschaltprinzip und verwendet direkt den Hauptthread
zur
Verarbeitung

Bei anderen wie „thenAccept“ und „thenAcceptAsync“, „thenApply“ und „thenApplyAsync“ usw. ist der Unterschied zwischen ihnen derselbe.

Ich denke du magst

Origin blog.csdn.net/qq_43649937/article/details/131901728
Empfohlen
Rangfolge