一、ThreadPoolTaskExecutor和ThreadPoolExecutor有何區別
- ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。
- ThreadPoolTaskExecutor是對ThreadPoolExecutor進行了封裝處理。
- ThreadPoolTaskExecutor這個類則是spring包下的,是sring爲我們提供的線程池類。
- ThreadPoolExecutor結構,祖類都是調用Executor接口,ThreadPoolTaskExecutor結構,祖類是調用Executor接口
二、ThreadPoolTaskExecutor 線程池等待所有任務完成的幾種方式
方式1【推薦】: 使用 CountDownLatch
public class TestThreadPool {
public static void main(String[] args) throws InterruptedException {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("xxxx-");
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(5);
threadPoolTaskExecutor.setQueueCapacity(5);
threadPoolTaskExecutor.setKeepAliveSeconds(300);
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
System.out.println("丟棄");
}
});
threadPoolTaskExecutor.initialize();
int taskNum = 10;
CountDownLatch latch = new CountDownLatch(taskNum);
for (int i=0; i < taskNum; i++) {
final int taskId = i;
System.out.println("-- submit task " + taskId);
threadPoolTaskExecutor.execute(() -> {
try {
System.out.printf("task: %s, thread: %s, start at %d\n", taskId, Thread.currentThread().getName(), System.currentTimeMillis()/1000);
Thread.sleep(2_000);
System.out.printf("task: %s, thread: %s, end at %d\n", taskId, Thread.currentThread().getName(), System.currentTimeMillis()/1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
latch.await();
threadPoolTaskExecutor.shutdown();
}
}
方式2: 使用 Future
方式3: setWaitForTasksToCompleteOnShutdown(true)
使用方式:
spring框架中的ThreadPoolTaskExecutor只是對jdk線程池ThreadPoolExecutor的包裝
第一種使用方法:
1、springboot啓動類中添加線程池配置
/**
* 定義線程池
* @return
*/
@Bean("testThreadPool")
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);//配置核心線程數
executor.setMaxPoolSize(20);//配置最大線程數
executor.setKeepAliveSeconds(5);
executor.setQueueCapacity(200);//配置隊列大小
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//拒絕策略
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();//執行初始化
return executor;
}
2、注入ThreadPoolTaskExecutor對象
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
3、其他方法和jdk的線程池使用方式相同。
具體參考:https://blog.csdn.net/sumengnan/article/details/105156397中的demo
第二種使用方法:
1、springboot啓動類中添加線程池配置和@EnableAsync註解
/**
* 定義線程池
* @return
*/
@Bean("testThreadPool")
public Executor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);//配置核心線程數
executor.setMaxPoolSize(20);//配置最大線程數
executor.setKeepAliveSeconds(5);
executor.setQueueCapacity(200);//配置隊列大小
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//拒絕策略
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();//執行初始化
return executor;
}
2、業務方法上使用@Async註解
package com.sumengnan.test.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class TestServiceImpl {
//volatile保證了靜態變量的可見性(visibility),
public static volatile AtomicInteger i = new AtomicInteger(0);
/**
* 無返回結果
*/
@Async("testThreadPool")
public void test() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("當前第"+ i.incrementAndGet() +"次執行");
}
/**
* 有返回結果
* @return
*/
@Async("testThreadPool")
public Future<String> test2() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new AsyncResult("當前第"+ i.incrementAndGet() +"次執行");
}
}
3、直接調用異步方法即可
package com.sumengnan.test.web;
import com.sumengnan.test.service.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
@RestController
public class TestController {
@Autowired
private TestServiceImpl testServiceImpl;
/**
* 無返回值
* @param request
*/
@GetMapping("test")
public void test(HttpServletRequest request){
TestServiceImpl.i=new AtomicInteger(0);
//循環230次
for (int i = 0; i <230; i++) {
System.out.println("第"+(i+1)+"個任務加入隊列");
testServiceImpl.test();
}
System.out.println("已全部加入隊列");
}
/**
* 有返回值
* @param request
*/
@GetMapping("test2")
public void test2(HttpServletRequest request) throws ExecutionException, InterruptedException {
TestServiceImpl.i=new AtomicInteger(0);
//循環230次
for (int i = 0; i <230; i++) {
System.out.println("第"+(i+1)+"個任務加入隊列");
Future<String> future = testServiceImpl.test2();
System.out.println(future.get());
}
System.out.println("已全部加入隊列");
}
}
看結果:
注意:
關於@Async註解失效需要注意以下幾點:
1. 註解的方法必須是public方法。
2. 方法一定要從另一個類中調用,也就是從類的外部調用,類的內部調用是無效的。因爲@Transactional和@Async註解的實現都是基於Spring的AOP,而AOP的實現是基於動態代理模式實現的。那麼註解失效的原因就很明顯了,有可能因爲調用方法的是對象本身而不是代理對象,因爲沒有經過Spring容器。
3. 異步方法使用註解@Async的返回值只能爲void或者Future。