[英]Variable callables with different return types
The problem I have in hand is that I methods one(), two(), three(), four()
that have a different return types say, A, B, C, D
and I need to spawn variable numbers of threads (one for each method depending on the use case. This means I would want to call a subset of methods at a time.) Now, am using the cachedThreadPool
to submit these callables. 我手头的问题是我的方法
one(), two(), three(), four()
有不同的返回类型,比如A, B, C, D
和我需要产生可变数量的线程(每个方法一个,具体取决于用例。这意味着我希望一次调用一个方法的子集。)现在,我使用cachedThreadPool
来提交这些callables。 Some code below: 下面的一些代码:
public class Dispatcher {
public void dispatch(List<MethodNames> methodNames) {
//Now I am going to iterate through the list of methodNames
//And submit each method to the `ExecutorService`
for(MethodNames m : methodNames) {
switch(m) {
case ONE: //submit one()
//wait and get the future
Future<A> future = cachePool.submit(new Callable<A>() {
@Override
public A call() {
return one();
});
A response = future.get();
break;
....
}
}
}
}
public enum MethodNames {
ONE, TWO, THREE, FOUR
}
//Example methods:
public A one() {
}
public B two() {
}
My question is how do the above such that all the method calls are made without having to wait for one to finish. 我的问题是上面如何做所有的方法调用,而不必等待一个完成。 Also, how do I gather all the
futures
and wait for them to finish cause all the futures have a different generic type Future<A>, Future<B>
etc. I make the call to submit()
inside the case statement so I don't have the access to the returned Future<T>
outside the case. 另外,我如何收集所有
futures
并等待它们完成因为所有期货都有不同的泛型类型Future<A>, Future<B>
等我在case语句中调用submit()
所以我不要在案例之外无权访问返回的Future<T>
。 Now I could do an if, else
instead of the for
loop but I am trying to figure out if there is a better way to achieve this. 现在我可以做一个
if, else
而不是for
循环,但我想弄清楚是否有更好的方法来实现这一点。
I would do it this way - 我会这样做 -
I
. I
。 A
, B
, C
and D
implements I
. A
, B
, C
和D
实现I
。 valueOf
and object overriding
to remove case statement. valueOf
和对象overriding
来删除case语句。 I
from all the methods. I
A
, B
, C
, D
, I
) as they are plain class and interface - not doing much. A
, B
, C
, D
, I
),因为它们是普通类和接口 - 没有做太多。 Below is the code: 以下是代码:
Dispatcher.java
Dispatcher.java
package com.test.thread;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
public class Dispatcher {
public void dispatch() throws InterruptedException, ExecutionException {
Map<MethodNames, Future<I>> reponse = new HashMap<MethodNames, Future<I>>();
ExecutorService cachePool = Executors.newCachedThreadPool();
for (MethodNames methodNames : MethodNames.values()) {
Future<I> future = cachePool.submit(methodNames.worker());
reponse.put(methodNames, future);
}
cachePool.awaitTermination(5, TimeUnit.MINUTES);
for(MethodNames key : reponse.keySet()) {
I result = reponse.get(key).get();
System.out.println("result :: " + result);
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
new Dispatcher().dispatch();
}
} }
MethodNames.java
MethodNames.java
package com.test.thread;
import java.util.concurrent.*;
public enum MethodNames {
ONE {
@Override
public Callable<I> worker() {
return new Callable<I>() {
@Override
public I call() throws InterruptedException {
System.out.println("Thread1");
TimeUnit.SECONDS.sleep(30);
return new A();
}};
}
},
TWO {
@Override
public Callable<I> worker() throws InterruptedException {
return new Callable<I>() {
@Override
public I call() throws InterruptedException {
System.out.println("Thread2");
TimeUnit.SECONDS.sleep(30);
return new B();
}};
}
},
THREE {
@Override
public Callable<I> worker() throws InterruptedException {
return new Callable<I>() {
@Override
public I call() throws InterruptedException {
System.out.println("Thread3");
TimeUnit.SECONDS.sleep(30);
return new C();
}};
}
},
FOUR {
@Override
public Callable<I> worker() throws InterruptedException {
return new Callable<I>() {
@Override
public I call() throws InterruptedException {
System.out.println("Thread");
TimeUnit.SECONDS.sleep(30);
return new D();
}};
}
};
public abstract Callable<I> worker() throws InterruptedException;
} }
It seems best to split the get from the future, hence add a Callable as parameter to the enum. 最好将get与未来分开,因此将一个Callable作为参数添加到枚举中。 Then the enum instant can create a Future.
然后,枚举瞬间可以创造一个未来。 For generic typing unfortunately the resulting Class needs to be stored, and used to have a correct typing.
不幸的是,对于通用类型,需要存储生成的类,并用于正确键入。
public enum MethodNames {
ONE(A.class, () -> { one() }),
TWO(B.class, () -> { two() }),
...
FOUR(D.class, () -> { four() });
private final Class<?> resultType;
private final Future<?> future;
private <T> MethodNames(Class<T> resultType, Callable<T> callable) {
this.resultType = resultType;
future = cachePool.submit(callable);
}
public <T> T getResponse(Class<T> type) {
Object response = future.get();
return resultType.asSubclass(type).cast(response);
}
}
If these are the only Callables
that you're submitting to the ExecutorService
then you can call awaitTermination on cachePool
after submitting your jobs (which can be Runnable
instead of Callable
) 如果这些是您提交给
ExecutorService
的唯一Callables
,那么您可以在提交作业后调用cachePool上的cachePool
(可以是Runnable
而不是Callable
)
public class Dispatcher {
public void dispatch(List<MethodNames> methodNames) {
for(MethodNames m : methodNames) {
switch(m) {
case ONE: //submit one()
cachePool.execute(new Runnable() {
@Override
public void run() {
// do work
});
break;
....
}
}
}
cachePool.awaitTermination(100, TimeUnit.HOURS);
}
If there are other unrelated tasks in cachePool
or for some other reason you can't use awaitTermination
then you can block on a Semaphore instead. 如果
cachePool
有其他不相关的任务或由于某些其他原因你不能使用awaitTermination
那么你可以阻塞信号量 。 Initialize the Semaphore
with zero permits, each task will release
a permit when it completes, and the dispatch
method blocks on semaphore.acquire(methodNames.size())
which waits until all tasks have called release
(and hence completed). 使用零许可初始化
Semaphore
,每个任务在完成时release
许可,并且dispatch
方法在semaphore.acquire(methodNames.size())
上semaphore.acquire(methodNames.size())
,等待所有任务调用release
(并因此完成)。 Note the try-finally
block in the Runnable
, otherwise if the Runnable
throws an exception then it won't call release
and the dispatch
method will block forever. 注意
Runnable
的try-finally
块,否则如果Runnable
抛出异常,那么它将不会调用release
并且dispatch
方法将永远阻塞。
public class Dispatcher {
public void dispatch(List<MethodNames> methodNames) {
Semaphore semaphore = new Semaphore(0);
for(MethodNames m : methodNames) {
switch(m) {
case ONE: //submit one()
cachePool.execute(new Runnable() {
@Override
public void run() {
try {
// do work
} finally {
semaphore.release();
}
});
break;
....
}
}
}
semaphore.acquire(methodNames.size());
}
If you're collecting the results of the tasks (it doesn't look like you're doing this at the moment, but requirements tend to change) then each Runnable
can store its result in a shared ConcurrentLinkedQueue or some other thread-safe data structure (or one data structure per return type etc), and then dispatch
can process these results when the semaphore.acquire
or awaitTermination
method unblocks. 如果你正在收集任务的结果(看起来你现在看起来不像这样,但需求往往会改变),那么每个
Runnable
都可以将其结果存储在共享的ConcurrentLinkedQueue或其他一些线程安全的数据中结构(或每个返回类型的一个数据结构等),然后当semaphore.acquire
或awaitTermination
方法解除awaitTermination
时, dispatch
可以处理这些结果。
You need to approach the result in multiple steps: 您需要以多个步骤处理结果:
In this case you can just use Future<?>
in the signature, because just for waiting you don't have to know about the result type. 在这种情况下,您可以在签名中使用
Future<?>
,因为只是等待您不必了解结果类型。 So you could just create a method void waitForAll(List< Future<?> > futures)
. 所以你可以创建一个方法
void waitForAll(List< Future<?> > futures)
。
For this you need some kind of handle that knows about the type the Future
will provide. 为此你需要一些知道
Future
将提供的类型的句柄。 Because of Java's type erasure this handle has to store the Class<T>
somehow. 由于Java的类型擦除,这个句柄必须以某种方式存储
Class<T>
。 So the most simple handle would be the according Future<T>
itself (with T
being one of A
and B
in your example). 所以最简单的句柄就是相应的
Future<T>
本身(在你的例子中T
是A
和B
之一)。
So you could just store the futures in a Map<Class<?>, Future<?>)
(or a MultiMap
) with an additional get method of the type Future<T> get<T>(Class<T> handle)
. 因此,您可以将期货存储在
Map<Class<?>, Future<?>)
(或MultiMap
)中,并使用Future<T> get<T>(Class<T> handle)
类型的附加get方法。
You might replace your enum MethodNames
with this handle. 您可以使用此句柄替换enum
MethodNames
。
Map
/ MultiMap
as defined above, eg by unsing multiple calls to your dispatch
method. Map
/ MultiMap
,例如通过取消对dispatch
方法的多次调用。 Map
/ MultiMap
with the get method described above Map
/ MultiMap
获取相应的结果 You are trying to do something like fork-join or map-reduce kind of stuff. 你正在尝试做一些像fork-join或map-reduce这样的东西。 You may find an established mechanism to do this, instead of reinventing the wheel.
您可能会找到一种既定机制来完成此任务,而不是重新发明轮子。
Anyway getting back to your specific problem of waiting for all methods to finish and moving on: 无论如何回到你等待所有方法完成和继续前进的具体问题:
As you mentioned you should not lose the pointer to future itself. 正如你所提到的,你不应该失去指向未来的指针。 So create a structure Result , which could hold all the futures.
因此,创建一个结构Result ,它可以容纳所有未来。 wait on the Result in current thread.
等待当前线程中的Result 。 Run another thread inside Result which will monitor futures and notify when all methods are returned.
在Result中运行另一个线程,它将监视期货并在返回所有方法时通知 。
When Result notifies, you move ahead in the current thread with all returned data held by the Result object. 当Result通知时,您将在当前线程中向前移动,并返回Result对象保存的所有返回数据。
Simple (limited) solution : this is really useful if you can define an interface / super class for the return values (similar to the sql.ResultSet
), otherwise not so much... then the switch appears again when working with the results because you have to cast it. 简单(有限)解决方案 :如果您可以为返回值定义接口/超类(类似于
sql.ResultSet
),这非常有用,否则没有那么多......然后在处理结果时再次出现切换因为你必须施展它。
Dispatch: 调度:
dispatcher.dispatch(new SuperABC[] {new A(), new B(), new C()});
// dispatcher.dispatch(new A(), new B(), new C()); with ... notation, see comment below
Interfaces: 接口:
public interface Result { ... }
public interface SuperABC extends Callable<Result> {}
Class example: 类示例:
public class A implements SuperABC {
public Result call() throws Exception { ... }
}
dispatch method: 调度方式:
public Result[] dispatch(SuperABC[] tasks) { // dispatch(SuperABC... tasks)
List<Future<Result>> refs = new ArrayList<Future<Result>>(tasks.length);
Result[] ret = new Result[tasks.length];
for (SuperABC task : tasks)
refs.add(cachedThreadPool.submit(task));
for (int i = 0; i < tasks.length; i++)
ret[i] = refs.get(i).get();
return ret;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.