Java 使用ExecutorService时出现超时问题
我有一个数十万可调用对象的列表。当每一个都运行时,它会根据给定的值执行可能很长的计算。因此,我希望异步运行每个任务(最好通过使用某种执行器),并在30秒后检索每个计算的结果,取消那些没有及时完成的计算。(获得的值用于其他地方。) 到目前为止,我是这样实现的:Java 使用ExecutorService时出现超时问题,java,multithreading,concurrency,executorservice,Java,Multithreading,Concurrency,Executorservice,我有一个数十万可调用对象的列表。当每一个都运行时,它会根据给定的值执行可能很长的计算。因此,我希望异步运行每个任务(最好通过使用某种执行器),并在30秒后检索每个计算的结果,取消那些没有及时完成的计算。(获得的值用于其他地方。) 到目前为止,我是这样实现的: private void process() { class Runner implements Callable<BigDecimal> { final int x, y; Runne
private void process() {
class Runner implements Callable<BigDecimal> {
final int x, y;
Runner(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public BigDecimal call() {
BigDecimal gWidth = xMax.subtract(xMin), gHeight = yMax.subtract(yMin);
BigDecimal gy = gHeight.multiply(BigDecimal.valueOf(-y)).divide(BigDecimal.valueOf(height)).add(yMax);
BigDecimal gx = gWidth.multiply(BigDecimal.valueOf(x)).divide(BigDecimal.valueOf(width)).add(xMin);
// The calculation begins when this method is called
BigDecimal result = calculateAt(gx, gy);
return result;
}
}
ExecutorService exec = Executors.newCachedThreadPool();
List<Runner> runners = new ArrayList<>();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
runners.add(new Runner(x, y));
}
}
try {
List<Future<BigDecimal>> results = exec.invokeAll(runners, 30, TimeUnit.SECONDS);
for (Future<BigDecimal> future : results) {
// Check if the future's task was cancelled and process the results
}
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
exec.shutdown();
}
// Extra variables and methods
BigDecimal xMin = BigDecimal.valueOf(-7),
xMax = BigDecimal.valueOf(7),
yMin = BigDecimal.valueOf(-7),
yMax = BigDecimal.valueOf(7);
int height = 850, width = 850;
private BigDecimal calculateAt(BigDecimal x, BigDecimal y) {
try {
// Just to simulate a potential execution time
Thread.sleep((ThreadLocalRandom.current().nextInt(45) + 1) * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return BigDecimal.ONE;
}
private void进程(){
类运行程序实现了可调用的{
最终整数x,y;
流道(内部x,内部y){
这个.x=x;
这个。y=y;
}
@凌驾
公共BigDecimal调用(){
BigDecimal gWidth=xMax.subtract(xMin),gHeight=yMax.subtract(yMin);
BigDecimal gy=gHeight.multiply(BigDecimal.valueOf(-y)).divide(BigDecimal.valueOf(height)).add(yMax);
BigDecimal gx=gWidth.multiply(BigDecimal.valueOf(x)).divide(BigDecimal.valueOf(width)).add(xMin);
//当调用此方法时,计算开始
BigDecimal结果=计算(gx,gy);
返回结果;
}
}
ExecutorService exec=Executors.newCachedThreadPool();
List runner=new ArrayList();
对于(int y=0;y
ArrayListrunners
存储每个要执行的Callable
,然后将其发送到ExecutorService以运行所有任务。我遇到的问题是,任务似乎是同步启动的,在超时30秒后,只有前4万或5万个任务完成了,更不用说开始执行了
似乎正在发生的是,
ExecutorService.invokeAll
方法只允许列表中的所有任务在30秒的时间内开始和完成执行。相反,我需要的是在每个任务的基础上开始这个30秒的窗口,也就是说,一旦任务已经开始,就给它30秒的时间让它完成invokeAll
似乎不能做到这一点,至少在newCachedThreadPool
中不能做到这一点。是否有Java库或其他实现方法可以这样做?最简单的方法是单独调用每个任务,例如在初始化运行程序的循环中。由于您上面提到的行为符合,所以没有子类(至少是Java标准版附带的子类)的行为会有所不同。实现这种行为的独特函数也不存在
从#invokeAll(List的文档中,我认为您可以用解决这个问题
例如,以下是基于问题片段的代码:
private static final ExecutorService executor = Executors.newCachedThreadPool();
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(100);
private static void processAsync() {
List<CompletableFuture<Object>> futureList = IntStream.range(0, height).boxed()
.flatMap(y -> IntStream.range(0, width).boxed().map(x -> new Runner(x, y)))
.map(runner ->
CompletableFuture.anyOf(
CompletableFuture.supplyAsync(runner, executor),
timeout(Duration.ofSeconds(30))
).exceptionally(throwable -> {
// timeout is handled here
return BigDecimal.ZERO;
})
)
.collect(Collectors.toList());
CompletableFuture.allOf(futureList.toArray(new CompletableFuture<?>[0]))
.thenAccept(v -> {
List<BigDecimal> results = futureList.stream()
.map(CompletableFuture::join)
.map(r -> (BigDecimal) r)
.collect(Collectors.toList());
// process final results
BigDecimal sum = results.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("Final sum: " + sum);
})
.exceptionally(throwable -> {
System.out.println("Failed allOf with " + throwable);
return null;
});
}
private static CompletableFuture<BigDecimal> timeout(Duration duration) {
CompletableFuture<BigDecimal> future = new CompletableFuture<>();
scheduler.schedule(
() -> future.completeExceptionally(new TimeoutException("Timeout " + Thread.currentThread().getName())), duration.toMillis(), MILLISECONDS);
return future;
}
private static class Runner implements Supplier<BigDecimal> {...
private static final executor service executor=Executors.newCachedThreadPool();
private static final ScheduledExecutorService scheduler=Executors.newScheduledThreadPool(100);
私有静态void processAsync(){
List futureList=IntStream.range(0,高度).boxed()
.flatMap(y->IntStream.range(0,宽度).boxed().map(x->newrunner(x,y)))
.map(跑步者->
完全的未来(
CompletableFuture.SupplySync(运行者、执行者),
超时(持续时间秒(30))
).例外情况下(可丢弃->{
//超时在这里处理
返回BigDecimal.ZERO;
})
)
.collect(Collectors.toList());
CompletableFuture.allOf(futureList.toArray(新的CompletableFuture[0]))
.然后接受(v->{
列表结果=futureList.stream()
.map(CompletableFuture::join)
.map(r->(BigDecimal)r)
.collect(Collectors.toList());
//处理最终结果
BigDecimal sum=results.stream().reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println(“最终总和:+总和”);
})
.例外情况下(可丢弃->{
System.out.println(“失败的allOf,带“+可丢弃”);
返回null;
});
}
专用静态CompletableFuture超时(持续时间){
CompletableFuture=新的CompletableFuture();
时间表(
()->future.completeException(新的TimeoutException(“Timeout”+Thread.currentThread().getName())、duration.toMillis()、毫秒);
回归未来;
}
私有静态类运行程序实现供应商{。。。
这里的主要思想是使用CompletableFuture.anyOf
并将其应用于有用任务的CompletableFuture
和超时任务的CopmpletableFuture
。超时是通过使用ScheduledExecutorService
和CompletableFuture.completeetException
实现的。因此,基本上,anyOf
returns result或TimeoutException
我会尝试一下。小错误:supplyAsync(runner,executor)
应该是supplyAsync(()->runner,executor)
。无需在第二个IntStream
上使用boxed()
。两个问题:这是否保留了R的顺序