[英]Can I use CompletableFuture in Scheduling Tasks with Spring?
問題:
我的應用程序正在使用 Spring Boot 和 Java 8。我有一個計划任務來從三個不同的數據源獲取一些數據,但現在它們正在按順序運行。 我想通過同時使用 CompletableFuture 划分三個數據檢索操作來加速它們。 但是,我運行代碼並發現 CompletableFuture 任務在一個名為“scheduling-1”的單個線程中運行,而不是異步運行。
我試過的:
我以為我在做一些不太好的事情。 所以,我決定退后一步,通過運行一個小型測試項目來嘗試一下。 我嘗試過的就像我結合了以下兩個示例提到的技術:
但是當我把它們放在一起時,我發現它們在調用 Async 方法時使用的是同一個線程。
output 是:
[ scheduling-1] Looking up PivotalSoftware, thread: scheduling-1
[ scheduling-1] Looking up CloudFoundry, thread: scheduling-1
[ scheduling-1] Looking up Spring-Projects, thread: scheduling-1
我的問題:
是我的代碼出錯了,還是調度程序中只有一個線程可用,即使我使用 Async for CompletableFuture? 我的實驗結果背后的根本原因是什么?
我的代碼:
異步方法應用程序
@SpringBootApplication
@EnableAsync
@EnableScheduling
@PropertySource("classpath:common.properties")
public class AsyncMethodApplication {
public static void main(String[] args) {
// close the application context to shut down the custom ExecutorService
SpringApplication.run(AsyncMethodApplication.class, args).close();
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
}
我的服務
@Service
public class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private UserLookupService gitHubLookupService;
@Scheduled(cron = "${cron.expression.reloadUserData}")
@PostConstruct
public void loadCurrentDayUsers() throws Exception {
this.loadUsers();
// this.loadOthers();
}
public void loadUsers() throws Exception {
// Start the clock
long start = System.currentTimeMillis();
// Kick of multiple, asynchronous lookups
CompletableFuture<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
CompletableFuture<User> page2 = gitHubLookupService.findUser("CloudFoundry");
CompletableFuture<User> page3 = gitHubLookupService.findUser("Spring-Projects");
// Wait until they are all done
CompletableFuture.allOf(page1,page2,page3).join();
// Print results, including elapsed time
logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
logger.info("--> " + page1.get());
logger.info("--> " + page2.get());
logger.info("--> " + page3.get());
}
}
用戶
@JsonIgnoreProperties(ignoreUnknown=true)
public class User {
private String name;
private String blog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBlog() {
return blog;
}
public void setBlog(String blog) {
this.blog = blog;
}
@Override
public String toString() {
return "User [name=" + name + ", blog=" + blog + "]";
}
}
用戶查找服務
@Service
public class UserLookupService {
private static final Logger logger = LoggerFactory.getLogger(UserLookupService.class);
@Async
public CompletableFuture<User> findUser(String user) throws InterruptedException {
logger.info("Looking up " + user + ", thread: " + Thread.currentThread().getName());
// mock result
User results = new User();
results.setName(user);
results.setBlog("Blog - " + user);
// Artificial delay of 1s for demonstration purposes
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
common.properties
cron.expression.reloadUserData=0 0/1 3-23 * * MON-SAT
歸功於 M. Deinum。
“刪除@PostConstruct,因為這可能會導致您的 UserLookupService 急切初始化導致沒有代理,因此沒有 @Async 行為”
這是一個有效的答案。
你可以參考:
@Service
public class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
@Autowired
private UserLookupService gitHubLookupService;
@Scheduled(cron = "${cron.expression.reloadUserData}")
@PostConstruct
public void loadCurrentDayUsers() throws Exception {
this.loadUsers();
// this.loadOthers();
}
@Autowired
@Qualifier("threadTaskGithubLookup")
private ThreadPoolTaskExecutor threadTaskGithubLookup;
public void loadUsers() throws Exception {
// Start the clock
long start = System.currentTimeMillis();
List<String> stringList = new ArrayList<>();
stringList.add("PivotalSoftware");
stringList.add("CloudFoundry");
stringList.add("Spring-Projects");
List<CompletableFuture<User>> lstComplet = new ArrayList<>();
for (String str : stringList) {
lst.add(CompletableFuture.runAsync(() -> {
try {
lstComplet.add(doExecuted(str));
} catch (Exception e){
log.error("Exception {}", e.getMessage());
}
}, threadTaskGithubLookup));
}
List<User> userResults = new ArrayList<>();
for(CompletableFuture<User> compl : lstComplet){
User user = compl.get();
userResult.add(user);
}
// Print results, including elapsed time
logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
for(User us : userResults){
logger.info("--> " + new ObjectMapper().writeValueAsString(us));
}
}
private CompletableFuture<User> doExecuted(String str) {
User user = gitHubLookupService.findUser(str);
return CompletableFuture.completedFuture(user);;
}
我重新聲明了 bean Executor:
@Bean("threadTaskGithubLookup")
public ThreadPoolTaskExecutor threadTaskGithubLookup() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.