1.ウェッジ
我々のシステムでは、多くの場合、複数のスレッドを使用してと思う当然、時間のかかるタスクを扱う、JDKは、なぜそれをマルチスレッド達成するために春を使用し、非常に便利な操作スレッドのAPIを提供してくれましたか?
ネイティブJDKの並行処理APIを使用したよりも簡単1.春。(注
@Async
取得する)
2.我々は通常統合アプリケーション環境の春、私たちの春の豆でも管理できるようにし、その後、マルチスレッド、単純よりエレガントを達成するために春を使用しています。
なぜ非同期でしょうか?あなたは、複数のサービスを呼び出す必要がある場合には、従来の同期呼び出しを使用して実行する場合、これは
サービスコール
待機サービスA応答
呼サービスB
サービス応答Bを待っているが
、サービスCを呼び出し
応答Cである待機サービス
を終了するサービスA、サービスBおよびサービスCからビジネスロジックに従って、完成し戻され、その後
各サービスは、3秒の応答時間が必要なので、実行ダウンのためならば、それはビジネスロジックを完了するために、以上9秒かかる場合がありますが、我々は非同期呼び出しを使用する場合
サービスへのコール
コールサービスBが
サービスCを呼び出し
、次にサービスA、B及びCからの応答を待つ
Cが返され、サービスA、サービスBおよびサービスからのデータのビジネスロジックを完了するために、そして終了します
同じビジネスロジックを完了するために理論的に約3秒
複数のスレッド2.春のブートを使用する方法
マルチスレッドでの春は、単にコンフィギュレーションクラスに追加し、実際には非常に簡単である@EnableAsync
複数のスレッドを使用することができます。あなたが実行する方法を併用することで@Async
、ねじタスクに定義することができます。スプリングによって、私たちが提供するThreadPoolTaskExecutor
スレッドプールを使用することができます。
2.1最初のステップは、最初のようなスプリングブートメインカテゴリにスレッドプールを定義します。
@Configuration
@EnableAsync // 启用异步任务
public class AsyncConfiguration {
// 声明一个线程池(并指定线程池的名字)
@Bean("taskExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数5:线程池创建时候初始化的线程数
executor.setCorePoolSize(5);
//最大线程数5:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(5);
//缓冲队列500:用来缓冲执行任务的队列
executor.setQueueCapacity(500);
//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("DailyAsync-");
executor.initialize();
return executor;
}
}
あなたが設定することができ、多くのものがあります。デフォルトでは、それが使用していますSimpleAsyncTaskExecutor
。
2.2スレッドプールを使用して、第2のステップ、
スレッドプールを定義した後、どのように我々はそれを実行するスレッドプールのリソースを使用してタスクを実行するための非同期呼び出しを行うのですか?この方法は非常に簡単です、我々は唯一の必要な@Async
スレッドプールの名前を指定するなど、注釈を付けることができます。
@Service
public class GitHubLookupService {
private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);
@Autowired
private RestTemplate restTemplate;
// 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行(并指定线程池的名字)
@Async("taskExecutor")
public CompletableFuture<String> findUser(String user) throws InterruptedException {
logger.info("Looking up " + user);
String url = String.format("https://api.github.com/users/%s", user);
String results = restTemplate.getForObject(url, String.class);
// Artificial delay of 3s for demonstration purposes
Thread.sleep(3000L);
return CompletableFuture.completedFuture(results);
}
}
春findUser方法は、それは別のスレッドで実行されることを、@Asyncアノテーションをマークされています。メソッドの戻り型はCompleetableFuture代わりに非同期サービスにおける要件である文字列です。
2.3第3のステップと、テストユニット
最後に、我々は確認するためにユニットテストを書きます
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class AsyncTests {
private static final Logger logger = LoggerFactory.getLogger(AsyncTests.class);
@Autowired
private GitHubLookupService gitHubLookupService;
@Test
public void asyncTest() throws InterruptedException, ExecutionException {
// Start the clock
long start = System.currentTimeMillis();
// Kick of multiple, asynchronous lookups
CompletableFuture<String> page1 = gitHubLookupService.findUser("PivotalSoftware");
CompletableFuture<String> page2 = gitHubLookupService.findUser("CloudFoundry");
CompletableFuture<String> page3 = gitHubLookupService.findUser("Spring-Projects");
// Wait until they are all done
//join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行
CompletableFuture.allOf(page1,page2,page3).join();
// Print results, including elapsed time
float exc = (float)(System.currentTimeMillis() - start)/1000;
logger.info("Elapsed time: " + exc + " seconds");
logger.info("--> " + page1.get());
logger.info("--> " + page2.get());
logger.info("--> " + page3.get());
}
}
上記のユニットテストの実装すべての出力が定義される前に、我々が開始する前に、我々は、コンソールスレッドプールのスレッド名の接頭辞で見ることができる、と9秒未満の実行時間、テストは、我々は成功し、非同期タスクを実行するスレッドプールを使用していることを示しますアップ!
3.ノート
春を使用する場合の溶液の非同期マルチスレッドのマルチスレッド化問題頻繁に遭遇リターン失敗は次のとおりです。
そして、非同期メソッド呼び出し方法の必須写在不同的类中
クラスで記述された場合、影響はありません!
理由:
スプリングのクラスは、@Transactionalアノテーションはメソッドをスキャンするとき、ばね@Transactionalアノテーションを有し、同様の問題を抱えている場合、プロキシクラスを生成するとき、プロキシクラストランザクション開閉に、同じクラスで、クラスメソッド呼び出しは、インビボで実行されます春には、メソッドの呼び出しをインターセプトすることはできません。