常见的两种创建线程的方式。一种是直接继承Thread,另外一种就是实现Runnable接口。
这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
Future模式的核心思想是能够让主线程将原来需要同步等待的这段时间用来做其他的事情。(因为可以异步获得执行结果,所以不用一直同步等待去获得执行结果)。

这里给一个实际使用的案例代码,理论是一样的,但是编码方式有多种。
关于代码的注意点和说明,都在注释里面,由于案例比较简单,这里不再啰嗦。
package com.example.springboot;
import com.alibaba.fastjson2.JSON;
import com.example.springboot.entity.User;
import com.example.springboot.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Supplier;
/**
* Future 机制测试
*/
@SpringBootTest
public class FutureTest {
@Autowired
private UserMapper userMapper;
private static ThreadPoolExecutor pool;
static {
pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
}
@Test
public void test1() throws Exception {
// 创建一个调用次数为2的计数工具
CountDownLatch countDownLatch = new CountDownLatch(2);
// 直接在异步内组装返回数据,注意List不是线程安全的,也可以采用Vector或者synchronized关键字方式
List<User> listTmp = new CopyOnWriteArrayList<>();
// 通过异步返回接收,因为是编码依次接收,所以不存在安全问题
List<User> listRe = new ArrayList<>();
// 异步执行 1
CompletableFuture<User> reStrFu1 = CompletableFuture.supplyAsync(new Supplier<User>() {
@Override
public User get() {
try {
Thread.sleep(2000);
// 获取结果方式1,直接组装到上层方法变量中
User user = userMapper.selectById(1);
listTmp.add(user);
return user;
}catch (Exception e){
e.printStackTrace();
}finally {
countDownLatch.countDown(); // 异步执行结束
}
return null;
}
}, pool);
// 异步执行 2
CompletableFuture<User> reStrFu2 = CompletableFuture.supplyAsync(new Supplier<User>() {
@Override
public User get() {
try {
Thread.sleep(2000);
// 获取结果方式1,直接组装到上层方法变量中
User user = userMapper.selectById(2);
listTmp.add(user);
return user;
}catch (Exception e){
e.printStackTrace();
}finally {
countDownLatch.countDown(); // 异步执行结束
}
return null;
}
}, pool);
// // 获取结果方式2,带超时的参数
// listRe.add(reStrFu1.get(10, TimeUnit.SECONDS)); // 可以定义获取超时时间
// listRe.add(reStrFu2.get(10, TimeUnit.SECONDS)); // 可以定义获取超时时间
// 获取结果方式3,等待计数结束
try {
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
listRe.add(reStrFu1.get());
listRe.add(reStrFu2.get());
System.out.println("listTmp" + JSON.toJSONString(listTmp));
System.out.println("listRe" + JSON.toJSONString(listRe));
}
}最后打印结果是一样的,都是两条数据的JSON。
END
通过方式1获取返回结果时,如果使用的是ArrryList,那么很可能会少一条数据,这里需要特别注意。