使用 java 期货而不需要本地可变状态
Using java futures without requiring local mutable state
我编写了一些代码,主要负责通过我提供给客户的名为 "orchestrate" 的库方法按顺序编排多个 API(是的,我知道它很原始).这个编排方法背后的无非是一个循环,它按照收到的顺序执行 API,然后将其委托给许多 classes,其中包含一些用于构建的业务逻辑请求,调用 API,对响应执行一些验证并最终返回 api 结果。因此,如果客户端发送 api 列表:
{a1, a2, a3, a4, a5}
它将以完全阻塞的方式依次执行每个 api。
我正在尝试将其加强到可以并行调用多个 API 的位置,具体取决于我从客户接收指令的方式。把它想象成客户向我发送一个列表列表,如:{ {a1, a2}, {a3}, {a4, a5} }
这意味着我想并行执行 a1 和 a2(这意味着构建他们的请求,调用 apis,验证响应)。然后等到我确定他们都完成了。然后执行a3,等我确定完成了。最后我想执行 a4 和 a5 并遵循通常的模式。
现在,我很想使用 futures 作为它们提供的简单抽象来执行方法,等待使用 .get()
方法的响应。但是我注意到 executorService 需要的是 future 对 call()
方法的调用。这很好,但让我觉得每个 class 实现的 call()
方法可能需要访问 "local" 数据才能完成它的工作(毕竟,我不能通过call() 方法任何特定参数)。我真的想避免持有任何本地可变状态,因为这会带来自己的副作用。
有没有办法让我不持有本地状态,仍然使用期货来处理我的多线程用例?还是我对期货的理解完全错误,我遗漏了一些明显的东西?如果没有,是否有关于一些替代方案的好的前进道路的建议?
好的,所以你有一个 class 它以阻塞的方式从网页获取一些数据,并接受一些参数:
public class DataFetcher {
public Data fetchData(int param1, int param2) {
// ...
}
}
现在你想同时执行这个方法两次,然后取回期货。所以你只需要创建一个 Callable:
final DataFetcher fetcher = new DataFetcher();
Callable<Data> task1 = new Callable<>() {
@Override
public Data call() {
return fetcher.fetchData(1, 2);
}
};
Callable<Data> task2 = new Callable<>() {
@Override
public Data call() {
return fetcher.fetchData(3, 4);
}
};
Future<Data> result1 = executorService.submit(task1);
Future<Data> result2 = executorService.submit(task2);
我在这里没有看到任何可变状态。
为了避免重复代码和使用匿名classes,你可以定义一个顶层class:
public DataFetcherTask implements Callable<Data> {
private final DataFetcher fetcher;
private final int param1;
private final int param2;
public DataFetcherTask(DataFetcher fetcher, int p1, int p1) {
this.fetcher = fetcher;
this.param1 = p1;
this.param2 = p2;
}
@Override
public Data call() {
return fetcher.fetchData(param1, param2);
}
};
然后像这样使用它:
Future<Data> result1 = executorService.submit(new DataFetcherTask(fetcher, 1, 2));
Future<Data> result2 = executorService.submit(new DataFetcherTask(fetcher, 3, 4));
这里仍然没有可变状态的踪迹。
我编写了一些代码,主要负责通过我提供给客户的名为 "orchestrate" 的库方法按顺序编排多个 API(是的,我知道它很原始).这个编排方法背后的无非是一个循环,它按照收到的顺序执行 API,然后将其委托给许多 classes,其中包含一些用于构建的业务逻辑请求,调用 API,对响应执行一些验证并最终返回 api 结果。因此,如果客户端发送 api 列表:
{a1, a2, a3, a4, a5}
它将以完全阻塞的方式依次执行每个 api。
我正在尝试将其加强到可以并行调用多个 API 的位置,具体取决于我从客户接收指令的方式。把它想象成客户向我发送一个列表列表,如:{ {a1, a2}, {a3}, {a4, a5} }
这意味着我想并行执行 a1 和 a2(这意味着构建他们的请求,调用 apis,验证响应)。然后等到我确定他们都完成了。然后执行a3,等我确定完成了。最后我想执行 a4 和 a5 并遵循通常的模式。
现在,我很想使用 futures 作为它们提供的简单抽象来执行方法,等待使用 .get()
方法的响应。但是我注意到 executorService 需要的是 future 对 call()
方法的调用。这很好,但让我觉得每个 class 实现的 call()
方法可能需要访问 "local" 数据才能完成它的工作(毕竟,我不能通过call() 方法任何特定参数)。我真的想避免持有任何本地可变状态,因为这会带来自己的副作用。
有没有办法让我不持有本地状态,仍然使用期货来处理我的多线程用例?还是我对期货的理解完全错误,我遗漏了一些明显的东西?如果没有,是否有关于一些替代方案的好的前进道路的建议?
好的,所以你有一个 class 它以阻塞的方式从网页获取一些数据,并接受一些参数:
public class DataFetcher {
public Data fetchData(int param1, int param2) {
// ...
}
}
现在你想同时执行这个方法两次,然后取回期货。所以你只需要创建一个 Callable:
final DataFetcher fetcher = new DataFetcher();
Callable<Data> task1 = new Callable<>() {
@Override
public Data call() {
return fetcher.fetchData(1, 2);
}
};
Callable<Data> task2 = new Callable<>() {
@Override
public Data call() {
return fetcher.fetchData(3, 4);
}
};
Future<Data> result1 = executorService.submit(task1);
Future<Data> result2 = executorService.submit(task2);
我在这里没有看到任何可变状态。
为了避免重复代码和使用匿名classes,你可以定义一个顶层class:
public DataFetcherTask implements Callable<Data> {
private final DataFetcher fetcher;
private final int param1;
private final int param2;
public DataFetcherTask(DataFetcher fetcher, int p1, int p1) {
this.fetcher = fetcher;
this.param1 = p1;
this.param2 = p2;
}
@Override
public Data call() {
return fetcher.fetchData(param1, param2);
}
};
然后像这样使用它:
Future<Data> result1 = executorService.submit(new DataFetcherTask(fetcher, 1, 2));
Future<Data> result2 = executorService.submit(new DataFetcherTask(fetcher, 3, 4));
这里仍然没有可变状态的踪迹。