Future接口用于获取异步计算的结果,可通过get()获取结果、cancel()取消、isDone()判断是否完成等操作。
V get()
: 获取结果,若无结果会阻塞至异步计算完成
V get(long timeOut, TimeUnit unit)
:获取结果,超时返回null
boolean isDone()
:执行结束(完成/取消/异常)返回true
boolean isCancelled()
:任务完成前被取消返回true
boolean cancel(boolean mayInterruptRunning)
:取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
java的Timer类来进行定时调用,schedule(TimerTask task, long delay)
调度一个task,经过delay(ms)后开始进行调度,仅仅调度一次
参考
在 Java Future 上使用带有 isDone 和 Cancel 的轮询而不是阻塞 get
Future的isDone()方法结果为true代表了什么?
java Timer(定时调用、实现固定时间执行)
Java之Future(cancel,iSDone)
Java多线程任务超时结束的5种实现方法
操作
pom
<!--基于Springboot-->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.1.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<skipTests>true</skipTests>
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
java测试代码
package ***.cfg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* ExecutorPoolConfig 类说明:
*
* @author z.y
* @version v1.0
* @date 2022/7/27
*/
@Order
@Configuration
public class ExecutorPoolConfig {
private static final Logger log = LoggerFactory.getLogger(ExecutorPoolConfig.class);
@Bean ("taskNotifyThread")
public AsyncTaskExecutor taskNotifyThread(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(10);
executor.setKeepAliveSeconds(60);
executor.setQueueCapacity(1);
executor.setThreadNamePrefix("TASK-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
log.info("线程池-初始化");
return executor;
}
public void testPool(int size){
try {
size = Math.max(4,size);
log.info("开始测试 线程池,数量 {}",size);
AsyncTaskExecutor executor = taskNotifyThread();
Map<String, Future<Boolean>> map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
int index = i;
map.put("T-"+index,executor.submit(() -> testOne(index)));
}
log.info("任务 已提交 线程池,数量 {}",size);
Set<String> keySet = map.keySet();
List<String> doneList = new ArrayList<>(size);
// 方式1 使用 get 阻塞判断否结束--------------
Boolean b;
for (String key : keySet) {
b = map.get(key).get();
if(null != b){
log.info("get 阻塞判断是否结束,{}",b);
doneList.add(key);
}
}
// 方式1 使用 get 阻塞判断是结束--------------
// 方式2 使用 isDone 轮询判断是结束---------------
Future<Boolean> future;
while (keySet.size() != doneList.size()){
for (String key : keySet) {
// 已结束的子任务 直接跳过
if(doneList.contains(key)){ continue; }
future = map.get(key);
if(future.isDone()){
doneList.add(key);
log.info("子任务已结束,{},{}",key,future.get());
}else{
log.info("子任务未结束-===-{}",key);
}
}
log.info("问了一遍,结束的任务:{},先睡1s",doneList);
TimeUnit.SECONDS.sleep(1);
}
// 方式2 使用 isDone 轮询判断是结束---------------
log.info("都已经处理结束,结束的顺序:{}",doneList);
}catch (Exception e){
log.error("任务测试失败",e);
}
}
private Boolean testOne(int index){
try {
int i = ThreadLocalRandom.current().nextInt(2,10);
log.info("子任务-{}-开始,睡眠:{}",index,i);
TimeUnit.SECONDS.sleep(i);
log.info("子任务-{}=====结束,睡眠:{}",index,i);
return i % 2 == 0;
}catch (Exception e){
log.error("子任务测试失败",e);
}
return false;
}
@PostConstruct
public void init(){
log.info("配置类初始化完成");
new Timer().schedule(new TimerTask() {
@Override
public void run() {
log.info("线程池开始处理");
testPool(6);
}
}, 2000);
}
}
日志
方式2-轮询处理
2022-07-28 08:54:55.722 INFO 17952 --- [ main] ***.cfg.ExecutorPoolConfig : 线程池-初始化
2022-07-28 08:54:55.723 INFO 17952 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'taskNotifyThread'
2022-07-28 08:54:55.992 INFO 17952 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9980 (http) with context path '/runner-demo'
2022-07-28 08:54:56.002 INFO 17952 --- [ main] ***.RunnerSpringDemoApplication : Started RunnerSpringDemoApplication in 2.32 seconds (JVM running for 4.784)
2022-07-28 08:54:56.004 INFO 17952 --- [ main] ***.RunnerSpringDemoApplication : http://127.0.0.1:9980/runner-demo/
2022-07-28 08:54:57.714 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 线程池开始处理
2022-07-28 08:54:57.714 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 开始测试 线程池,数量 6
2022-07-28 08:54:57.715 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 任务 已提交 线程池,数量 6
2022-07-28 08:54:57.716 INFO 17952 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-4-开始,睡眠:2
2022-07-28 08:54:57.716 INFO 17952 --- [ TASK-3] ***.cfg.ExecutorPoolConfig : 子任务-3-开始,睡眠:3
2022-07-28 08:54:57.716 INFO 17952 --- [ TASK-5] ***.cfg.ExecutorPoolConfig : 子任务-5-开始,睡眠:9
2022-07-28 08:54:57.716 INFO 17952 --- [ TASK-1] ***.cfg.ExecutorPoolConfig : 子任务-0-开始,睡眠:2
2022-07-28 08:54:57.716 INFO 17952 --- [ TASK-2] ***.cfg.ExecutorPoolConfig : 子任务-2-开始,睡眠:4
2022-07-28 08:54:57.716 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-2
2022-07-28 08:54:57.716 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-1
2022-07-28 08:54:57.716 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-4
2022-07-28 08:54:57.716 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-3
2022-07-28 08:54:57.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:54:57.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-0
2022-07-28 08:54:57.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[],先睡1s
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-2
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-1
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-4
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-3
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-0
2022-07-28 08:54:58.717 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[],先睡1s
2022-07-28 08:54:59.717 INFO 17952 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-4=====结束,睡眠:2
2022-07-28 08:54:59.717 INFO 17952 --- [ TASK-1] ***.cfg.ExecutorPoolConfig : 子任务-0=====结束,睡眠:2
2022-07-28 08:54:59.717 INFO 17952 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-1-开始,睡眠:4
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-2
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-1
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务已结束,T-4,true
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-3
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务已结束,T-0,true
2022-07-28 08:54:59.718 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0],先睡1s
2022-07-28 08:55:00.717 INFO 17952 --- [ TASK-3] ***.cfg.ExecutorPoolConfig : 子任务-3=====结束,睡眠:3
2022-07-28 08:55:00.719 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-2
2022-07-28 08:55:00.719 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-1
2022-07-28 08:55:00.719 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务已结束,T-3,false
2022-07-28 08:55:00.719 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:55:00.719 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3],先睡1s
2022-07-28 08:55:01.716 INFO 17952 --- [ TASK-2] ***.cfg.ExecutorPoolConfig : 子任务-2=====结束,睡眠:4
2022-07-28 08:55:01.720 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务已结束,T-2,true
2022-07-28 08:55:01.720 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-1
2022-07-28 08:55:01.720 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:55:01.720 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2],先睡1s
2022-07-28 08:55:02.721 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-1
2022-07-28 08:55:02.721 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:55:02.721 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2],先睡1s
2022-07-28 08:55:03.718 INFO 17952 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-1=====结束,睡眠:4
2022-07-28 08:55:03.721 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务已结束,T-1,true
2022-07-28 08:55:03.721 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:55:03.721 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1],先睡1s
2022-07-28 08:55:04.722 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:55:04.722 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1],先睡1s
2022-07-28 08:55:05.723 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务未结束-===-T-5
2022-07-28 08:55:05.723 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1],先睡1s
2022-07-28 08:55:06.716 INFO 17952 --- [ TASK-5] ***.cfg.ExecutorPoolConfig : 子任务-5=====结束,睡眠:9
2022-07-28 08:55:06.723 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 子任务已结束,T-5,false
2022-07-28 08:55:06.723 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1, T-5],先睡1s
2022-07-28 08:55:07.724 INFO 17952 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 都已经处理结束,结束的顺序:[T-4, T-0, T-3, T-2, T-1, T-5]
方式1-阻塞获取
--2022-07-29 10:19:25.436 - INFO 19288 --- [ main] ***.cfg.ExecutorPoolConfig : 线程池-初始化
--2022-07-29 10:19:25.438 - INFO 19288 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'taskNotifyThread'
--2022-07-29 10:19:25.819 - INFO 19288 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9980 (http) with context path '/runner-demo'
--2022-07-29 10:19:25.831 - INFO 19288 --- [ main] ***.RunnerSpringDemoApplication : Started RunnerSpringDemoApplication in 3.103 seconds (JVM running for 6.078)
--2022-07-29 10:19:25.834 - INFO 19288 --- [ main] ***.RunnerSpringDemoApplication : http://127.0.0.1:9980/runner-demo/
--2022-07-29 10:19:27.424 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 线程池开始处理
--2022-07-29 10:19:27.424 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 开始测试 线程池,数量 6
--2022-07-29 10:19:27.427 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 任务 已提交 线程池,数量 6
--2022-07-29 10:19:27.427 - INFO 19288 --- [ TASK-5] ***.cfg.ExecutorPoolConfig : 子任务-5-开始,睡眠:6
--2022-07-29 10:19:27.427 - INFO 19288 --- [ TASK-2] ***.cfg.ExecutorPoolConfig : 子任务-2-开始,睡眠:2
--2022-07-29 10:19:27.427 - INFO 19288 --- [ TASK-1] ***.cfg.ExecutorPoolConfig : 子任务-0-开始,睡眠:7
--2022-07-29 10:19:27.427 - INFO 19288 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-4-开始,睡眠:2
--2022-07-29 10:19:27.427 - INFO 19288 --- [ TASK-3] ***.cfg.ExecutorPoolConfig : 子任务-3-开始,睡眠:7
--2022-07-29 10:19:29.429 - INFO 19288 --- [ TASK-2] ***.cfg.ExecutorPoolConfig : 子任务-2=====结束,睡眠:2
--2022-07-29 10:19:29.429 - INFO 19288 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-4=====结束,睡眠:2
--2022-07-29 10:19:29.429 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : get 阻塞判断是否结束,true
--2022-07-29 10:19:29.429 - INFO 19288 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-1-开始,睡眠:3
--2022-07-29 10:19:32.430 - INFO 19288 --- [ TASK-4] ***.cfg.ExecutorPoolConfig : 子任务-1=====结束,睡眠:3
--2022-07-29 10:19:32.430 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : get 阻塞判断是否结束,false
--2022-07-29 10:19:32.431 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : get 阻塞判断是否结束,true
--2022-07-29 10:19:33.429 - INFO 19288 --- [ TASK-5] ***.cfg.ExecutorPoolConfig : 子任务-5=====结束,睡眠:6
--2022-07-29 10:19:34.429 - INFO 19288 --- [ TASK-1] ***.cfg.ExecutorPoolConfig : 子任务-0=====结束,睡眠:7
--2022-07-29 10:19:34.429 - INFO 19288 --- [ TASK-3] ***.cfg.ExecutorPoolConfig : 子任务-3=====结束,睡眠:7
--2022-07-29 10:19:34.429 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : get 阻塞判断是否结束,false
--2022-07-29 10:19:34.429 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : get 阻塞判断是否结束,true
--2022-07-29 10:19:34.429 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : get 阻塞判断是否结束,false
--2022-07-29 10:19:34.429 - INFO 19288 --- [ Timer-0] ***.cfg.ExecutorPoolConfig : 都已经处理结束,结束的顺序:[T-2, T-1, T-4, T-3, T-5, T-0]
-
结束
get阻塞方式,任务不是最早结束的就能得到结果值,是按照自定义key的 顺序获取结果。
isDone轮询方式,任务是在主线程循环判断,结果顺序是和任务耗时有关,代码较多。
个人见解,如有问题请指出。