春ブーツ非同期スレッド


一般的な背景管理システムは、レポートのエクスポート機能を持つレポートのエクスポートのために管理者などの大量のデータを、通常より時間がかかりは、輸出へのボタンをクリックすると、多くの場合、我々は成功し、次のステップにレポートをエクスポートできるようになるまでに長い時間を待たなければなりません明らかに、このような同期した方法は、需要を満たすために失敗しました。実際の開発は、現在一般的に使用されている方法は、エクスポート時間のかかる作業を完了するために、輸出のために他のシステムにメッセージを送信するJMSメッセージキューの方法を使用するか、またはプロジェクト内の非同期スレッドをオンにすることです。本論文では、春ブーツのいくつかは、非同期スレッドを可能にする方法を説明するために、輸出のシナリオを報告します。

利用可能なカスタムスレッドプールと非同期可能
スプリング存在を非同期インターフェイススレッドプールを構成するために使用されるインターフェイスAsyncConfigurerインタフェースを、それは二つの方法、getAsyncExecutorとgetAsyncUncaughtExceptionHandlerを有し、第一の方法は、スレッドプールを得ることで、第二方法は、非同期スレッドで発生した異常を処理するために使用されます。次のようにそのソースは以下のとおりです。

package org.springframework.scheduling.annotation;

import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.lang.Nullable;

public interface AsyncConfigurer {

    // 获取线程池
    @Nullable
    default Executor getAsyncExecutor() {
        return null;
    }

    // 异步异常处理器
    @Nullable
    default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}


私は、非同期スレッド機構を開きたいので、ここでのインターフェースは、達成空にされている提供し、その後、我々は、手動でこのインタフェースを実装する必要があり、Spring構成クラスラベルされたインタフェースを実装するクラスは、その後、春の非同期が利用可能である開かれましたばねはgetAsyncExecutorが非同期動作を実行するために使用されるスレッドによって得られるであろう、もちろん、非同期2つの注釈を結合全体を開く必要があり、1 @EnableAsync、他方は@Asyncでは、第一の構成クラスでマークされています春は非同期的に利用可能である告げることで、第二のコメントは通常、1つの方法でマークされているこのメソッドを呼び出すとき、あなたはそれを実行するスレッドプールから新しいスレッドを取得します。
今度は、スレッドプールと非同期のオープンは、次のように、コードをAsyncConfigurerを達成するために、プロファイルクラスAsyncConfigを書くために、ここで、利用可能である定義してみましょう:

package cn.itlemon.springboot.async.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:28
 */
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        // 自定义线程池
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        taskExecutor.setCorePoolSize(10);
        // 最大线程数
        taskExecutor.setMaxPoolSize(30);
        // 线程队列最大线程数
        taskExecutor.setQueueCapacity(2000);
        // 初始化线程池
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> {
            log.error("Error Occurs in async method:{}", ex.getMessage());
        };
    }
}



第一の方法我々は、スレッドプールを定義し、このようなコアのスレッドの数、スレッドの最大数、スレッドキューとスレッドの最大数のようないくつかの基本的なパラメータを設定し、第二の方法は、非同期例外処理スレッド、ありますインタフェースは、ラムダ式は、本明細書の実装クラスと略記用いるようAsyncUncaughtExceptionHandler、(抽象のみ1つのインターフェース方法は、一般@FunctionalInterface注釈インターフェースに使用表記)を関数であるので、例外ハンドラは、達成するために、クラス・オブジェクトAsyncUncaughtExceptionHandlerインターフェースを返しますオブジェクト、非同期例外処理はログについてはこちらを記録し、ラムダ式に精通していない、次のように、あなたが直接、実装クラスのオブジェクトAsyncUncaughtExceptionHandlerを作成するために、匿名の内部クラスを使用することができれば、他の論理演算をしませんでした。

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

    return new AsyncUncaughtExceptionHandler() {
        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            log.error("Error Occurs in async method:{}", ex.getMessage());
        }
    };
}


もう一つ注意すべきは、私たちが春のコンフィギュレーションクラスを登録、その後、上記の構成クラスで@EnableAsyncノートを追加したことである春豆の時間であり、それが可能な非同期のメカニズムを開きます。

利用可能テスト非同期メカニズム
のレポートが表示され生成するためのサービスレイヤインタフェースを書くこと:

package cn.itlemon.springboot.async.service;

import java.util.concurrent.Future;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:32
 */
public interface AsyncService {

    /**
     * 模拟生成报表的异步方法
     */
    void generateReport();

}


その実装クラスは次のとおりです。

package cn.itlemon.springboot.async.service.impl;

import cn.itlemon.springboot.async.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.Future;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:33
 */
@Service
public class AsyncServiceImpl implements AsyncService {

    @Override
    @Async
    public void generateReport() {
        // 模拟异步生成报表代码,这里设置为打印
        System.out.println("报表线程名称:【" + Thread.currentThread().getName() + "】");
    }
    
}


そう、メソッドを呼び出すときに、春にはこの方法を実行するための新しいスレッドを取得しますが、作業は簡単で、シミュレーション、およびマーク@Async注釈方法に使用print文ので、レポートをエクスポートされているものとしますここでは、現在の方法のスレッドの実行の名前をプリントアウトします。次のように我々は、コントローラを記述します。

package cn.itlemon.springboot.async.controller;

import cn.itlemon.springboot.async.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @author jiangpingping
 * @date 2018/10/30 19:36
 */
@RestController
@RequestMapping("/async")
@Slf4j
public class AsyncController {

    private final AsyncService asyncService;

    @Autowired
    public AsyncController(AsyncService asyncService) {
        this.asyncService = asyncService;
    }

    @GetMapping("/page")
    public String asyncPage() {
        System.out.println("当前请求线程名称为:【" + Thread.currentThread().getName() + "】");
        // 异步调用
        asyncService.generateReport();
        // 返回结果
        return "async";
    }
    
}


我々はまた、現在のコントローラプロセスにおける現在のスレッドを印刷するプロジェクトを実行し、指定したURLへのアクセス、新しいスレッドが有効になっているかどうかをgenerateReportメソッドを呼び出すときに比較することができます。私たちは、春のブートアプリケーションを起動し、ブラウザのアドレスバーにします。http:// localhost:8080 /非同期/ページ、結果がコンソールに出力されます。

現在のリクエストスレッド名:[HTTP-NIO-8080-exec- 1 ]
レポートスレッド名:[ThreadPoolTask​​Executor-1]は

明らかに、これは成功し、我々はオープン非同期スレッドという、同じスレッドではありません。

非同期スレッドの例外処理
、典型的には春は、2つのカテゴリーに分け、非同期スレッドの例外処理は、一方が非同期メソッドは値を返さないであり、他方は非同期メソッドの戻り値です。

第1の方法は値を返さない
コードの非同期スレッドの例外が発生したときに最初のクラス、我々はすなわちgetAsyncUncaughtExceptionHandler方法を達成するために、AsyncConfig設定クラス設定にされているノーリターン値のために、あること、私たちは、このメソッドを呼び出します。 generateReport AsyncServiceImplの我々の方法を手動ラインのSystem.out.println(1/0)を追加し、テストするための例外処理は、次のように、ゼロ例外にそれを引き起こし、コードは次のとおりです。

@Override
@Async
public void generateReport() {
    // 模拟异步生成报表代码,这里设置为打印
    System.out.println("报表线程名称:【" + Thread.currentThread().getName() + "】");
    System.out.println(1 / 0);
}



します。http:// localhost:ブラウザのアドレスバーに再び春ブートアプリケーションを起動すると8080 /非同期/ページそれがないので、それは、例外が異なるスレッドで発生しているため、例外は、非同期プロセスで発生しますメインスレッドの実行後の効果、及び例外が発生は、getAsyncUncaughtExceptionHandlerが設定方法は、例外が処理され、処理モードは、ログ記録を使用することです。

10 2018-10-31:57である:ERROR 2391 --- 09.952 cispringboot.async.config.AsyncConfig [-lTask​​Executor 1]:エラー非同期メソッドで発生:/ ZEROによって
1
番目のクラスは、メソッドの戻り値を有する
第二のケースのための非同期メソッドが値を返すこと、そしてどのように我々は、非同期スレッド処理の戻り値を取得します、通常の方法では、インタフェースの未来を使用する非同期メソッドの値を返すことです、ListenableFutureまたはクラスAsyncResultのパッケージは、すぐにジェネリックとして値を返します。言ったインターフェイスまたはクラスに渡します。ここでは、簡単に、彼らがソースコード内で使用される方法を検討します。

将来のインタフェース:

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);
    
    boolean isCancelled();
    
    boolean isDone();
    
    V get() throws InterruptedException, ExecutionException;
    
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}



分析の方法:

それは、ミッションに失敗したキャンセルするfalseを返す場合、タスクが正常に真のリターンをキャンセルした場合、タスクをキャンセルする方法をキャンセルします。MayInterruptIfRunningパラメータは、実行されるが、完了していない、真設定した場合、タスクが実行される過程でキャンセルすることができるタスクをキャンセルできるようにするかどうかを示します。タスクが完了している場合は、関係なく、mayInterruptIfRunningはキャンセルが偽のタスクのリターンを完了している場合、この方法は確かに、あること、falseを返すtrueまたはfalseであり、タスクが実行されている場合は、falseにmayInterruptIfRunningセットtrueの場合、返品にmayInterruptIfRunningセットの場合はtrue 、falseが返され、タスクが実行されていない場合は、関係なく、mayInterruptIfRunningは、確かにtrueを返すtrueまたはfalseです。
isCancelledメソッドは、成功は、タスクが正常に完了する前に、それがtrueを返すキャンセルされた場合、タスクが正常にキャンセルされたかどうかを示します。
isDoneメソッドはtrueに復帰、タスクが完了した場合、タスクが完了したかどうかを示し、
GETメソッドは、結果を取得するために使用され、この方法は、障害物が生成されます、タスクを返す前に終了するまで待機します。
GET(ロングタイムアウト、TimeUnitでユニット)を取得するために使用しましたその結果、指定された時間内に、その結果、直接のリターンはnullを取得していない場合。
ListenableFutureインタフェース:

public interface ListenableFuture<T> extends Future<T> {

    void addCallback(ListenableFutureCallback<? super T> callback);

    void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback);

    default CompletableFuture<T> completable() {
        CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
        addCallback(completable::complete, completable::completeExceptionally);
        return completable;
    }
}


ListenableFutureは将来のインターフェースを継承し、それはまた、主に非同期コールバックサイトを追加するために使用される3つの追加の方法は、例外を処理し、非同期メソッドの戻り値を取得するために使用することができます追加します。AsyncResult ListenableFutureクラスが実装インタフェース、それはまた、すべてのメソッドを実装します。次に、我々は非同期処理と例外処理の戻り値をキャプチャする方法を紹介します。

インタフェースの今後の使用

以下のように、ReturnMessage()、および今後のパッケージへのインタフェースを使用しています。私たちはAsyncServiceインターフェイスメソッドを追加します。

/ **
 *メソッドの非同期コールバック・メッセージ
 *
 * @return文字列
 * /
将来の<string> ReturnMessage();

実装コードのクラスは次の通りであります:

@Override
@Async
public Future<String> returnMessage() {
    System.out.println(Thread.currentThread().getName());
    String message = "Async Method Result";
    return new AsyncResult<>(message);
}


次のようにコントローラ層は、未来のオブジェクトクラスを達成するために取得することができます。

@GetMapping("/page1")
public String asyncPage1() {
    try {
        System.out.println(Thread.currentThread().getName());
        Future<String> result = asyncService.returnMessage();
        System.out.println(result.get());
    } catch (ExecutionException | InterruptedException e) {
        log.error("发生了异常:{}", e.getMessage());
    }
    return "async";
}


ここで、非同期なってください... catchの例外処理は、また、非同期メソッドの戻り値を取得するにはgetメソッドの未来を使用しますが、このアクイジション・モードでは、戻り値は、getメソッドを呼び出した後、と言うことです、現在のスレッドをブロックします実行が完了する前に、非同期スレッドがコードの次のラインを待ちます。

使用ListenableFutureインタフェース

以下のように、returnMsg()、およびListenableFutureパッケージへのインタフェースを使用しています。私たちはAsyncServiceインターフェイスメソッドを追加します。

/**
 * 异步回调消息方法
 *
 * @return 字符串
 */
ListenableFuture<String> returnMsg();


実装クラスのコードは次のよう:

@Override
@Async
public ListenableFuture<String> returnMsg() {
    System.out.println(Thread.currentThread().getName());
    String message = "Async Method Result";
    return new AsyncResult<>(message);
}


次のようにコントローラ層は、ListenableFutureクラスオブジェクトを取得するために達成することができます。

@GetMapping("/page2")
public String asyncPage2() {
    System.out.println(Thread.currentThread().getName());
    ListenableFuture<String> result = asyncService.returnMsg();
    result.addCallback(new SuccessCallback<String>() {
        @Override
        public void onSuccess(String result) {
            System.out.println("返回的结果是:" + result);
        }
    }, new FailureCallback() {
        @Override
        public void onFailure(Throwable ex) {
            log.error("发生了异常:{}", ex.getMessage());
        }
    });
    return "async";
}


それは上記のコードから分かるように、2つのコールバックを追加するオブジェクトクラスを達成するために、それぞれ、結果が返され、コールバックインターフェースSuccessCallback成功したコールバック・クラス・オブジェクトを達成するために失敗インターフェースFailureCallback異常の非同期処理の非同期処理が発生します。ListenableFutureインタフェースは、将来のインタフェースの拡張である支持体には、コールバック、効果的に問題をブロックしたスレッドを避けるため、それは、未来のインターフェイスの実装を監視します完了したら、それは治療の成功するonSuccessメソッドを呼び出します、異常が発生した場合に、あること、ONFAILURE方法は、例外処理と呼ばれています。比較して、より多くの非同期処理の戻り値を持つようにListenableFutureをお勧めします。Java1.8のために、実際には、より多くまたはグアバListenableFutureのCompletableFutureをお勧めします、興味のある学生は、詳細な研究を行うことができ、彼らはハンドル、非同期に、より強力な能力になります
 

公開された48元の記事 ウォン称賛26 ビュー70000 +

おすすめ

転載: blog.csdn.net/qq_38316721/article/details/104883281