场景:本地服务,需批量调用远程服务,各远程服务之间的结果互不影响。由于远程调用属于IO服务,因此调用服务越多,会导致响应结果越久
环境模拟
- 模拟远程接口
public interface RemoteLoader {
/**
* 远程接口方法
*/
String load();
}
- 远程服务实现类
/**
* 账户服务
*/
public class AccountService implements RemoteLoader {
/**
* 获取账户金额
*/
@Override
public String load() {
MockUtils.delay();
return MockUtils.getData().toString();
}
}
/**
* 人员信息服务
*/
public class EmployeeService implements RemoteLoader {
/**
* 获取入职时间信息
*
* @return
*/
@Override
public String load() {
MockUtils.delay();
return MockUtils.getInfo();
}
}
异步方法比较
以下列出4种调用方式
- 同步方式;
- java8并行流;
- java7 future方式;
- Java8 CompletableFuture;
在使用future时,注意线程池的创建管理
模拟本地客户端,调用远程方法
/**
* 模拟本地客户端,批量调用远程接口
*/
public class LocalClient {
final int cores = Runtime.getRuntime().availableProcessors() * 2;
/**
* 远程接口集合
*/
private final List<RemoteLoader> remoteLoaders = Arrays.asList(new AccountService(), new EmployeeService());
/**
* 创建线程池
*/
private final ExecutorService executorService = Executors.newFixedThreadPool(remoteLoaders.size(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
});
private final ExecutorService executorService2 = new ThreadPoolExecutor(cores, cores, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1024),
new NamedThreadFactory("test-executor", true), new ThreadPoolExecutor.AbortPolicy());
/**
* 同步方法
*/
public List<String> findSequential() {
return remoteLoaders.stream().map(RemoteLoader::load).collect(Collectors.toList());
}
/**
* java8并行流
*/
public List<String> findParallel() {
return remoteLoaders.parallelStream().map(RemoteLoader::load).collect(Collectors.toList());
}
/**
* java7 future
*/
public List<String> findFuture() {
// 整理future集合
List<Future<String>> futureList = remoteLoaders.stream().map(loader -> executorService.submit(loader::load)).collect(Collectors.toList());
// 从future获取各线程结果
List<String> collect = futureList.stream().map(future -> {
try {
return future.get(2, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程异常,关闭
future.cancel(true);
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList());
return collect;
}
/**
* java8 CompletableFuture
*/
public List<String> findCompletableFuture() {
// 整理future集合
List<CompletableFuture<String>> futureList = remoteLoaders.stream()
.map(loader -> CompletableFuture.supplyAsync(loader::load, executorService2))
.map(future -> future.thenApply(i -> i + "test"))
.collect(Collectors.toList());
// 从future获取各线程结果
List<String> collect = futureList.stream().map(CompletableFuture::join).collect(Collectors.toList());
return collect;
}
}
测试对比响应时间
public class CompareMethodMain {
private static final LocalClient localClient = new LocalClient();
public static void main(String[] args) {
execute("sync", () -> localClient.findSequential());
execute("parallel", () -> localClient.findParallel());
execute("future", () -> localClient.findFuture());
execute("comFuture", () -> localClient.findCompletableFuture());
}
private static void execute(String msg, Supplier<List<String>> s) {
long start = System.nanoTime();
System.out.println("remote interface response = " + s.get());
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.println(msg + " done in " + duration + " msecs");
}
}
终端返回:
结论
- 在io密集型的方法里,建议使用CompletableFuture;
- 在计算密集型方法里,建议使用并行流;
- io密集型和计算密集型,在创建线程池时,核心数有区别;