マルチスレッド非同期オーケストレーションの例の詳細な説明

1. マルチスレッドの見直し

目次

1. マルチスレッドの見直し

1. スレッドを初期化する 4 つの方法

2. スレッドプールの 7 つのパラメータ

3. 4 つの一般的なタイプのスレッド プール

4. 開発でスレッド プールを使用する理由は何ですか?

2. CompletableFuture の非同期オーケストレーション

1. 非同期オブジェクトを作成する

2. 計算完了時のコールバックメソッド 

3.ハンドルメソッド

4. スレッドのシリアル化方法

5. 2 つのタスクの組み合わせ - 両方を完了する必要があります

6. 2 つのタスクの組み合わせ - 1 つは完了

7. マルチタスクの組み合わせ


 

1. スレッドを初期化する 4 つの方法

1)、スレッドを継承

Thread thread = new Thread01();
thread.start();

2)、Runnableインターフェイスを実装します。

Runnable01 runnable01 = new Runnable01();
new Thread(runnable01).start();

3) Callable インターフェイス + FutureTask を実装します (戻り結果を取得し、例外を処理できます)

FutureTask futureTask = new FutureTask<>(new Callable01());
new Thread(futureTask).start();
// 阻塞等待线程执行完成,获取返回结果
Integer i = (Integer) futureTask.get();

4)、スレッドプール

public static ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new Runnable01());

        次の 2 つの方法でスレッド プールを初期化します。

Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, 
違い:
 Thread と Runnable は戻り値を取得できませんが、Callable は戻り値を取得できます
Thread、Runnable、Callable はリソースを制御できませんが、スレッド プールはリソースを制御できます スレッド プールを
介してパフォーマンスが安定し、実行結果が安定しますも取得でき、例外をキャッチすることもできます。ただし、複雑なビジネス状況では、1 つの非同期呼び出しが別の非同期呼び出しの実行結果に依存する場合があります。

2. スレッドプールの 7 つのパラメータ

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

7 つの主要パラメータ: 

corePoolSize: コア スレッドの数、スレッド プールの作成後に準備が整い、非同期タスクの受信を待機しているスレッドの数。

MaximumPoolSize: スレッド、制御リソースの最大数

keepAliveTime: 生存時間。現在のスレッド数がコア スレッドの数より大きい場合、スレッドのアイドル時間が指定された keepAliveTime より大きい場合、スレッドは解放されます (最大スレッド数 - コア スレッドの数)。

単位:時間単位

BlockingQueue<Runnable> workQueue: ブロックキュー。タスクが多すぎる場合は、現在の数のタスクがキューに配置されます (Runable 実行を使用して送信されたタスク)。アイドル状態のスレッドがある場合、タスクはキューから取り出して実行されます。

ThreadFactory: スレッドを作成するためのファクトリ

RejectedExecutionHandler ハンドラー: キューがいっぱいの場合、指定された拒否ポリシーに従ってタスクの実行を拒否します。

無制限のキューとブロッキング キュー内の制限付きキュー:

Bounded queue : 固定サイズのキューです。たとえば、固定サイズの LinkedBlockingQueue が設定されるか、サイズ 0 の SynchronousQueue がプロデューサーとコンシューマー間の転送にのみ使用されます。

無制限のキュー: 固定サイズを持たないキューを指します。これらのキューの特徴は、オーバーフローするまで直接キューに入れることができることです。もちろん、現実にはこれほど大きな容量 (Integer.MAX_VALUE を超える) はめったにないため、ユーザー エクスペリエンスからは「無制限」に相当します。たとえば、固定サイズの LinkedBlockingQueue セットはありません。

*実行プロセス:

1. スレッド プールを作成し、コア数のコア スレッドを準備し、タスクを受け付ける準備をします。

2. 新しいタスクが到着すると、コアによって準備されたアイドル スレッドを使用して実行されます。

        (1) コアがいっぱいの場合、受信タスクはブロッキング キューに入れられます。アイドル状態のコアは、タスクの実行を取得するキューをブロックします。

        (2) ブロッキング キューがいっぱいの場合、新しいスレッドが直接オープンされて実行されますが、最大数は max で指定された数までしかオープンできません。

        (3)とmaxは全て実行されます。最大コア数のアイドル状態のスレッドは、keepAliveTime で指定された時間が経過すると自動的に破棄されます。最終的にはコアサイズを維持しました。

        (4) スレッド数が最大数に達し、新たなタスクが入ってきた場合は、reject で指定した拒否ポリシーに従って処理されます。

3. すべてのスレッドは指定されたファクトリによって作成されます

3. 4 つの一般的なタイプのスレッド プール

newCachedThreadPool: キャッシュ可能なスレッド プールを作成します。スレッド プールの長さが処理の必要性を超えた場合、アイドル状態のスレッドを柔軟にリサイクルできます。リサイクルする方法がない場合は、新しいスレッドが作成されます。

newFixedThreadPool: 同時スレッドの最大数を制御できる固定長のスレッド プールを作成し、超過したスレッドはキューで待機します。

newScheduledThreadPool: スケジュールされた定期的なタスクの実行をサポートするために、固定長のスレッド プールを作成します。

newSingleThreadExecutor: シングルスレッド スレッド プールを作成します。これは、タスクの実行に一意のワーカー スレッドのみを使用し、すべてのタスクが指定された順序 (FIFO、LIFO、優先順位) で実行されるようにします。

4. 開発でスレッド プールを使用する理由は何ですか?

  • リソース消費の削減: すでに作成されたスレッドを再利用することで、スレッドの作成と破棄によって生じる損失を削減します。
  • 応答速度の向上:スレッドプール内のスレッド数がスレッドプールの上限を超えていない場合、一部のスレッドが割り当てられたタスクを待っている状態となるため、タスクが来たときに新たなスレッドを作成せずに実行できるようになります。 。
  • スレッドの管理性の向上: スレッド プールは、現在のシステム特性に基づいてプール内のスレッドを最適化し、スレッドの作成と破棄によって生じるシステム オーバーヘッドを削減します。無制限にスレッドを作成および破棄すると、システム リソースが消費されるだけでなく、システムの安定性も低下します。統一された割り当てにはスレッド プールを使用してください。

2. CompletableFuture の非同期オーケストレーション

        ビジネス シナリオ: 製品詳細ページのクエリのロジックは比較的複雑で、一部のデータはリモートで呼び出す必要があるため、必然的に時間がかかります。

5ca33d7bce6d4506a6050cc827358db7.png

       製品詳細ページの各クエリが完了するまでに以下にマークされた時間が必要な場合、ユーザーが製品詳細ページのコンテンツを表示するまでに 5.5 秒かかります。明らかにこれは受け入れられません。複数のスレッドがこれら 6 つのステップを同時に完了した場合、応答が完了するまでに 1.5 秒しかかからない可能性があります。


        Future は、非同期計算の結果を記述するために Java 5 で追加されたクラスです。`isDone` メソッドを使用して計算が完了したかどうかを確認することも、`get` を使用して計算が完了して結果を返すまで呼び出しスレッドをブロックすることもできます。また、`cancel` メソッドを使用して実行を停止することもできます。タスク。

         「Future」および関連する使用方法はタスクを非同期に実行する機能を提供しますが、結果を取得するのは非常に不便であり、タスクの結果はブロックまたはポーリングによってのみ取得できます。ブロッキング方式は明らかに非同期プログラミングの本来の意図に反しています ポーリング方式では不要な CPU リソースを消費し、計算結果を時間内に取得できません なぜオブザーバー デザイン パターンを使用して計算時にリスナーに通知することが間に合わないのでしょうか結果は完成?毛織物?

         Node.js などの多くの言語は、コールバックを使用して非同期プログラミングを実装します。Netty などの一部の Java フレームワークは、Java の「Future」インターフェースを拡張し、「addListener」などの複数の拡張メソッドを提供します。Google guava は、一般的な拡張された Future も提供します。Scala は、使いやすく強力な Future/Promise 非同期プログラミングも提供します。モデル。

        オーソドックスなJavaクラスライブラリとしては、自社ライブラリの機能強化を何とかすべきでしょうか?

        Java 8 では、約 50 のメソッドを含む新しいクラスが追加されました: CompletableFuture は、非常に強力な Future 拡張機能を提供し、非同期プログラミングの複雑さを簡素化し、コールバックを介して関数型プログラミング機能を提供します。 CompletableFuture を変換および結合するためのメソッドが提供されます。CompletableFuture クラスは Future インターフェイスを実装しているため、以前と同様に `get` メソッドを使用してブロックまたはポーリングすることで結果を取得できますが、このメソッドは推奨されません。

        CompletableFutureとFutureTaskはいずれもFutureインタフェースの実装クラスに属しており、どちらもスレッドの実行結果を取得できます。

5329b3b6a74849139d7d57b8824a7687.png

1. 非同期オブジェクトを作成する

CompletableFuture には、非同期操作を作成するための 4 つの静的メソッドが用意されています。

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)

1. runXxxx は結果を返しませんが、supplyXxx は結果を取得できます。

2. カスタム スレッド プールを渡すことができます。それ以外の場合は、デフォルトのスレッド プールが使用されます。

2. 計算完了時のコールバックメソッド 

public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)

whenComplete は、正常および異常な計算結果 (成功コールバック) を処理でき、異常な状況 (例外コールバック) を例外的に処理できます。

whenComplete と whenCompleteAsync の違い:

         whenComplete: 現在のタスクを実行しているスレッドは、whenComplete のタスクを引き続き実行します。 

        whenCompleteAsync: 実行は、実行のために whenCompleteAsync タスクをスレッド プールに送信し続けます。

メソッドは Async で終了しません。これは、アクションが同じスレッドを使用して実行し、Async が他のスレッドを使用して実行する可能性があることを意味します (同じスレッド プールを使用する場合、同じスレッドによって実行用に選択されることもあります)。

public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("Main...Start...."+Thread.currentThread());
        // supplyAsync 可获取返回值
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread());
            int i = 10 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool).whenCompleteAsync((res,exception)->{
            // 当上面的任务执行完成,能得到结果和异常信息,但无法修改返回值
            System.out.println("异步任务完成,结果为:"+res+",异常为:"+exception);
        }).exceptionally(throwable -> {
            // 可以感知异常,同时返回默认值
            return 101;
        });
        Integer integer = result.get();
        System.out.println("Main...End...."+integer);

    }

結果:

Main...Start....Thread[main,5,main]
当前线程Thread[pool-1-thread-1,5,main]
异步任务完成,结果为:null,异常为:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
Main...End....101

ES6 の then と Promise の拒否に似ています。

3.ハンドルメソッド

public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 方法执行完成后的处理
         */
        System.out.println("Main...Start...."+Thread.currentThread());
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool).handle((res,throwable)->{
            // handle可获取到返回值并可进行修改,且可以感知异常,并修改返回值
            if(res!=null && throwable==null){
                return res;
            }else{
                return 0;
            }
        });
        Integer integer = result.get();
        System.out.println("Main...End...."+integer);

    }

結果:

Main...Start....Thread[main,5,main]
当前线程Thread[pool-1-thread-1,5,main]
Main...End....0

4. スレッドのシリアル化方法

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)


public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)

thenApply メソッド: スレッドが別のスレッドに依存している場合、前のタスクによって返された結果を取得し、現在のタスクの戻り値を返します。

thenAccept メソッド: 処理結果を消費します。タスクの処理結果を受け取り、結果を返さずに処理を実行します。

thenRun メソッド: 上記のタスクが完了している限り、thenRun が実行され、タスクが処理された後でのみ、thenRun の後続の操作が実行されます。

Async を使用すると、デフォルトで実行は非同期になります。以前と同じです。

上記の前提条件タスクが正常に完了する必要があります。

機能<? スーパーT?U> を拡張します

        T: 前のタスクによって返された結果のタイプ

        U: 現在のタスクの戻り値の型

public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * 线程串行化
         *  1、thenRunAsync:无法获取上一步的执行结果,无返回值
         *  .thenRunAsync(() -> {
         *             System.out.println("任务2启动了");
         *         },threadPool)
         *  2、thenAcceptAsync:能获取到上一步的返回值,无返回值
         *  .thenAcceptAsync(res -> {
         *             System.out.println("任务2启动了"+res);
         *         },threadPool)
         *  3、thenApplyAsync:既能获取到上一步的返回值,有返回值
         */
        System.out.println("Main...Start...."+Thread.currentThread());
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程" + Thread.currentThread());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, threadPool).thenApplyAsync(res->{
            System.out.println("任务2启动了"+res);
            return "hello,"+res;
        },threadPool);
        //future.get() 获取返回值,会阻塞
        System.out.println("Main...End...."+future.get());

    }

結果:

Main...Start....Thread[main,5,main]
当前线程Thread[pool-1-thread-1,5,main]
运行结果:5
任务2启动了5
Main...End....hello,5

5. 2 つのタスクの組み合わせ - 両方を完了する必要があります

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor)

このタスクをトリガーするには、両方のタスクを完了する必要があります。

thenCombine: 2 つの Future を結合し、2 つの Future の戻り結果を取得し、現在のタスクの戻り値を返します。

 thenAccept Both: 2 つの Future を結合し、2 つの Future タスクの戻り結果を取得し、値を返さずにタスクを処理します。

runAfter Both: 2 つの Future を結合します。Future の結果を取得する必要はありません。2 つの Future がタスクを処理した後でのみタスクを処理する必要があります。

:

まず 2 つのタスクを作成します

/**
 * 两个都完成
 */
System.out.println("Main...Start...."+Thread.currentThread());
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1开始" + Thread.currentThread().getId());
    int i = 10 / 2 ;
    System.out.println("任务1结束");
    return i;
}, threadPool);

CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务2开始"+Thread.currentThread().getId());
    System.out.println("任务2结束");
    return "Hello";
}, threadPool);

runAfter BothAsync を使用します (前のタスクの実行結果は取得できません)。

future01.runAfterBothAsync(future02,()->{
    System.out.println("任务3启动");
},threadPool);

// 结果:

Main...Start....Thread[main,5,main]
任务1开始12
任务1结束
任务2开始13
任务2结束
任务3启动

thenAccept BothAsync を使用します (前のタスクの実行結果は取得できますが、返すことはできません)。

future01.thenAcceptBothAsync(future02,(f1,f2)->{
    System.out.println("任务3开始----f1结果:"+f1+"===f2结果"+f2);
},threadPool);

// 结果:

Main...Start....Thread[main,5,main]
任务1开始12
任务1结束
任务2开始13
任务2结束
任务3开始----f1结果:5===f2结果Hello

thenCombineAsync を使用します (前のタスクの実行結果を取得し、結果を変更して返すことができます)。

CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
    return f1 +":"+ f2 + "--->Hello";
}, threadPool);
System.out.println("Main...End...."+future.get());

// 结果:
Main...Start....Thread[main,5,main]
任务1开始12
任务1结束
任务2开始13
任务2结束
Main...End....5:Hello--->Hello

6. 2 つのタスクの組み合わせ - 1 つは完了

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)

2 つのタスクのうちいずれかの将来のタスクが完了すると、そのタスクが実行されます。

applyToEither: 2 つのタスクのうちの 1 つが完了し、その戻り値を取得し、タスクを処理して新しい戻り値を受け取ります。acceptEither: 2 つのタスクのうち 1 つが完了し、その戻り値を取得してタスクを処理します。新しい戻り値はありません。runAfterEither: 2 つのタスクのうちの 1 つが完了し、将来の結果を取得してタスクを処理する必要がなく、戻り値もありません。

7. マルチタスクの組み合わせ

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

allOf: すべてのタスクが完了するまで待ちます

anyOf: 1 つのタスクが完了している限り

例:

ユーザーがホームページをロードするときのデータ取得をシミュレートする 3 つのタスクを作成します。

CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品图片信息");
    return "hello.jpg";
}, threadPool);

CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品属性");
    return "白色+1TB";
}, threadPool);

CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品介绍");
    return "Apple";
}, threadPool);

allOf を使用します:

CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);

allOf.get(); // 等待所有任务执行完成 返回结果

// 获取所有任务的执行结果
System.out.println("Main...End...."+futureImg.get()+"===>"+futureAttr.get()+"===>"+futureDesc.get());

// 结果:
Main...Start....Thread[main,5,main]
查询商品图片信息
查询商品属性
查询商品介绍
Main...End....hello.jpg===>白色+1TB===>Apple

anyOf を使用します:

CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
anyOf.get(); // 有一个任务执行完成 返回结果

// 获取所有任务的执行结果
System.out.println("Main...End...."+anyOf.get());

// 结果:
Main...Start....Thread[main,5,main]
查询商品图片信息
查询商品属性
Main...End....hello.jpg
查询商品介绍

 

 

おすすめ

転載: blog.csdn.net/weixin_53922163/article/details/128177316
おすすめ