spring boot(五)非同步呼叫
前言
spring boot中呼叫非同步並沒有想象中的那麼複雜,之前在實習過程中已經遇到了幾次非同步呼叫的需求,自己都沒能順利弄下來,這裡正好來一個總結
建立定時任務
有時候系統中需要定時做一些任務,實現定時任務也沒有想象中的複雜(畢竟只是實現hello world)
1、建立定時任務實現類
package com.learn.springbootScheduled.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ScheduledTasks {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:SS");
@Scheduled(fixedRate=5000)
public void reportCurrentTime() {
System.out.println("現在時間:"+dateFormat.format(new Date(System.currentTimeMillis())));
}
}
標記Component註解,將類交給spring管理。在方法上打上@Scheduled標籤,該註解常用有如下幾個屬性。
註解示例 | 作用 |
---|---|
@Scheduled(fixedRate=5000) | 上一次開始執行時間之後5秒再執行 |
@Scheduled(fixedDelay=5000) | 上一次執行完畢時間點之後5秒再執行 |
@Scheduled(initialDelay=1000,fixedRate=5000) |
第一次延遲1秒後執行,之後按fixedRate的規則每5秒執行一次 |
@Scheduled(cron="*/5*****") | 通過cron表示式定義規則 |
2、在主載入類上新增@EnableScheduling註解
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
測試結果
@Asyn實現非同步
spring boot中實現非同步操作也是非常容易的,比起之前什麼的master-work模式,什麼實現runnable介面,這個,已經非常簡潔了,廢話不多說,直接上示例,畢竟hello world級別的示例異常簡單。
ps:關於同步和非同步的區別,這裡我就不解釋了。
spring boot中我們只需要在方法上打上@Asyn標籤就能實現非同步操作。
非同步呼叫(非回撥)
1、編寫非同步邏輯實現類
package com.learn.springbootAsync.utils;
import java.util.Random;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
/**
*
* @author liman
* @createtime 2018年8月27日
* @contract 15528212893
* @comment: 非同步功能函式
*/
@Component
public class Task {
public static Random random = new Random();
@Async
public void doTaskOne() throws InterruptedException {
System.out.println("開始做任務一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("完成任務一,耗時:" + (end - start) + "毫秒");
}
@Async
public void doTaskTwo() throws InterruptedException {
System.out.println("開始做任務二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("完成任務二,耗時:" + (end - start) + "毫秒");
}
@Async
public void doTaskThree() throws InterruptedException {
System.out.println("開始做任務三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("完成任務三,耗時:" + (end - start) + "毫秒");
}
}
2、主載入類中加上@EnableAsync註解
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3、測試程式碼
@Test
public void test() throws Exception {
task.doTaskOne();
task.doTaskTwo();
task.doTaskThree();
//主執行緒暫停
Thread.sleep(1000);
}
測試結果:
每次執行,任務完成先後順序會不同。
@Async註解不能修飾靜態方法,否則不會生效。
非同步回撥
所謂的回撥就是主執行緒需要獲取非同步執行緒的返回結果,這個詳細內容後面需要參考Java 高併發的相關書籍(之前學習的,都他媽忘光了)。
1、回撥執行緒類
package com.learn.springbootAsync.utils;
import java.util.Random;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
/**
*
* @author liman
* @createtime 2018年8月27日
* @contract 15528212893
* @comment: 非同步功能函式
*/
@Component
public class TaskFuture {
public static Random random = new Random();
@Async
public Future<String> doTaskOne() throws InterruptedException {
System.out.println("開始做任務一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("完成任務一,耗時:" + (end - start) + "毫秒");
return new AsyncResult<String>("任務一完成");
}
@Async
public Future<String> doTaskTwo() throws InterruptedException {
System.out.println("開始做任務二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("完成任務二,耗時:" + (end - start) + "毫秒");
return new AsyncResult<String>("任務二完成");
}
@Async
public Future<String> doTaskThree() throws InterruptedException {
System.out.println("開始做任務三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println("完成任務三,耗時:" + (end - start) + "毫秒");
return new AsyncResult<String>("任務三完成");
}
}
2、主載入類中同樣需要@EnableAsync註解
3、測試程式碼
@Test
public void testFuture() throws Exception {
long start = System.currentTimeMillis();
Future<String> task1 = taskFuture.doTaskOne();
Future<String> task2 = taskFuture.doTaskTwo();
Future<String> task3 = taskFuture.doTaskThree();
while(true) {
if(task1.isDone() && task2.isDone() && task3.isDone()) {
// 三個任務都呼叫完成,退出迴圈等待
System.out.println("回撥執行緒已經全部執行結束");
break;
}
System.out.println("主執行緒正在進行其他操作");
Thread.sleep(1000);
}
long end = System.currentTimeMillis();
System.out.println("任務全部完成,總耗時:" + (end - start) + "毫秒");
}
測試結果:
其中的完成任務X語句就是執行緒回撥的結果 ,耗時統計就是主執行緒完成的工作,根據這個模板,主執行緒在完成呼叫其他執行緒的時候,還可以完成自己的一些邏輯。
使用自定義的執行緒池
spring boot中使用自定義的執行緒池,關於執行緒池的介紹,建議參考《Java 併發程式設計實戰》一書
1、首先在spring boot主類中定義一個執行緒池
package com.learn.springbootThreadPoolSelf;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@EnableAsync
@Configuration
class TaskPoolConfiguration{
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("taskExecutor-");
executor.setAwaitTerminationSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
}
這裡一大堆相關執行緒池屬性的設定,還是有必要了解的,在前面推薦的書籍中都有介紹,後面的部落格會總結到這一步,這裡暫時忽略相關屬性。
2、@Async中指定執行緒池名稱
package com.learn.springbootThreadPoolSelf.util;
import java.util.Random;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
*
* @author liman
* @createtime 2018年8月27日
* @contract 15528212893
* @comment:
*
*/
@Component
public class TaskThreadPool {
public static Random random = new Random();
@Async("taskExecutor")
public void doTaskOne() throws Exception {
System.out.println("開始做任務一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+" 完成任務一,耗時:" + (end - start) + "毫秒");
}
@Async("taskExecutor")
public void doTaskTwo() throws Exception {
System.out.println("開始做任務二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+" 完成任務二,耗時:" + (end - start) + "毫秒");
}
@Async("taskExecutor")
public void doTaskThree() throws Exception {
System.out.println("開始做任務三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(1000));
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+" 完成任務三,耗時:" + (end - start) + "毫秒");
}
}
@Async中指定了之前設定的執行緒池名稱。這些執行緒邏輯程式碼會自動加入到執行緒池中。
3、測試程式碼
@Test
public void test() throws Exception {
task.doTaskOne();
task.doTaskTwo();
task.doTaskThree();
Thread.currentThread().join();
Thread.sleep(2000);
}
執行結果:
執行緒名稱為設定的執行緒名,說明任務已經放到指定的執行緒池中進行執行。
總結
總體來說,到目前為止,5篇spring boot系列的部落格,都只是存在helloworld級別,對實際開發有一定參考,每篇部落格都有些不足,對於一些稍微麻煩的示例,都沒有進行深入實現。這五篇部落格也是每天在工作之餘完成的,沒有更多的時間進行修飾。後續依舊會總結spring boot的其他功能。目前這5篇基本總結了spring boot中比較常用的功能。
相關文章
- Spring Boot中如何優雅地實現非同步呼叫?Spring Boot非同步
- 你應該知道的幾種Spring boot非同步呼叫方式Spring Boot非同步
- 非同步呼叫和同步呼叫 及 spring的@Async註解非同步Spring
- Spring Boot使用@Async實現非同步呼叫:自定義執行緒池Spring Boot非同步執行緒
- 實戰Spring Boot 2.0系列(三) – 使用@Async進行非同步呼叫詳解Spring Boot非同步
- Spring Boot使用@Async實現非同步呼叫:使用Future以及定義超時Spring Boot非同步
- 實戰Spring Boot 2.0系列(三) - 使用@Async進行非同步呼叫詳解Spring Boot非同步
- Spring Boot 2.0(五):Docker Compose + Spring Boot + Nginx + Mysql 實踐Spring BootDockerNginxMySql
- Spring boot學習(五)Spring boot整合Mybatis Generator以及PageHelperSpring BootMyBatis
- Spring Boot 非同步框架的使用Spring Boot非同步框架
- Spring Boot (五)Spring Data JPA 操作 MySQL 8Spring BootMySql
- Spring Boot的五種部署方式Spring Boot
- Spring Boot 實現非同步事件EventSpring Boot非同步事件
- spring boot非同步方法@Async踩坑Spring Boot非同步
- Spring Boot 中如何支援非同步方法Spring Boot非同步
- 非spring boot (即spring) 使用/整合 Spring cloud Config 分散式配置中心Spring BootCloud分散式
- Spring / Spring boot 非同步任務程式設計 WebAsyncTaskSpring Boot非同步程式設計Web
- Spring Boot使用@Async實現非同步呼叫:ThreadPoolTaskScheduler執行緒池的優雅關閉Spring Boot非同步thread執行緒
- (第五講)自定義Spring Boot StarterSpring Boot
- Spring Boot @Async 非同步任務執行Spring Boot非同步
- 五、Spring Boot整合Spring Security之認證流程2Spring Boot
- Spring / Spring boot 基於註解非同步程式設計@AsyncSpring Boot非同步程式設計
- Spring Boot + Mybatis + Spring MVC環境配置(五):templates模板使用Spring BootMyBatisMVC
- Spring boot 非同步/定時任務/郵件Spring Boot非同步
- Spring Boot 參考指南(使用RestTemplate呼叫REST服務)Spring BootREST
- Spring Boot移除內嵌Tomcat,使用非web方式啟動Spring BootTomcatWeb
- spring-boot-route(五)整合Swagger生成介面文件SpringbootSwagger
- spring boot使用@Async非同步註解,原理+原始碼Spring Boot非同步原始碼
- Spring Boot 整合 Fisco Bcos(部署、呼叫區塊鏈合約)Spring Boot區塊鏈
- Spring Boot:Spring Boot配置MybatisSpring BootMyBatis
- Spring Boot:Spring Boot配置SwaggerSpring BootSwagger
- 同步非同步,阻塞非阻塞非同步
- 非同步、同步、阻塞、非阻塞非同步
- Spring Boot中五個設計模式最佳實踐Spring Boot設計模式
- 備忘錄五:Spring Boot + RabbitMQ 分散式事務Spring BootMQ分散式
- Spring Boot第五彈,WEB開發初瞭解~Spring BootWeb
- spring boot @Async非同步註解上下文透傳Spring Boot非同步
- Dubbo原始碼分析(十)同步呼叫與非同步呼叫原始碼非同步