[英]Variable callables with different return types
我手頭的問題是我的方法one(), two(), three(), four()
有不同的返回類型,比如A, B, C, D
和我需要產生可變數量的線程(每個方法一個,具體取決於用例。這意味着我希望一次調用一個方法的子集。)現在,我使用cachedThreadPool
來提交這些callables。 下面的一些代碼:
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() {
}
我的問題是上面如何做所有的方法調用,而不必等待一個完成。 另外,我如何收集所有futures
並等待它們完成因為所有期貨都有不同的泛型類型Future<A>, Future<B>
等我在case語句中調用submit()
所以我不要在案例之外無權訪問返回的Future<T>
。 現在我可以做一個if, else
而不是for
循環,但我想弄清楚是否有更好的方法來實現這一點。
我會這樣做 -
I
。 A
, B
, C
和D
實現I
。 valueOf
和對象overriding
來刪除case語句。 I
A
, B
, C
, D
, I
),因為它們是普通類和接口 - 沒有做太多。 以下是代碼:
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
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;
}
最好將get與未來分開,因此將一個Callable作為參數添加到枚舉中。 然后,枚舉瞬間可以創造一個未來。 不幸的是,對於通用類型,需要存儲生成的類,並用於正確鍵入。
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);
}
}
如果這些是您提交給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);
}
如果cachePool
有其他不相關的任務或由於某些其他原因你不能使用awaitTermination
那么你可以阻塞信號量 。 使用零許可初始化Semaphore
,每個任務在完成時release
許可,並且dispatch
方法在semaphore.acquire(methodNames.size())
上semaphore.acquire(methodNames.size())
,等待所有任務調用release
(並因此完成)。 注意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());
}
如果你正在收集任務的結果(看起來你現在看起來不像這樣,但需求往往會改變),那么每個Runnable
都可以將其結果存儲在共享的ConcurrentLinkedQueue或其他一些線程安全的數據中結構(或每個返回類型的一個數據結構等),然后當semaphore.acquire
或awaitTermination
方法解除awaitTermination
時, dispatch
可以處理這些結果。
您需要以多個步驟處理結果:
在這種情況下,您可以在簽名中使用Future<?>
,因為只是等待您不必了解結果類型。 所以你可以創建一個方法void waitForAll(List< Future<?> > futures)
。
為此你需要一些知道Future
將提供的類型的句柄。 由於Java的類型擦除,這個句柄必須以某種方式存儲Class<T>
。 所以最簡單的句柄就是相應的Future<T>
本身(在你的例子中T
是A
和B
之一)。
因此,您可以將期貨存儲在Map<Class<?>, Future<?>)
(或MultiMap
)中,並使用Future<T> get<T>(Class<T> handle)
類型的附加get方法。
您可以使用此句柄替換enum MethodNames
。
Map
/ MultiMap
,例如通過取消對dispatch
方法的多次調用。 Map
/ MultiMap
獲取相應的結果 你正在嘗試做一些像fork-join或map-reduce這樣的東西。 您可能會找到一種既定機制來完成此任務,而不是重新發明輪子。
無論如何回到你等待所有方法完成和繼續前進的具體問題:
正如你所提到的,你不應該失去指向未來的指針。 因此,創建一個結構Result ,它可以容納所有未來。 等待當前線程中的Result 。 在Result中運行另一個線程,它將監視期貨並在返回所有方法時通知 。
當Result通知時,您將在當前線程中向前移動,並返回Result對象保存的所有返回數據。
簡單(有限)解決方案 :如果您可以為返回值定義接口/超類(類似於sql.ResultSet
),這非常有用,否則沒有那么多......然后在處理結果時再次出現切換因為你必須施展它。
調度:
dispatcher.dispatch(new SuperABC[] {new A(), new B(), new C()});
// dispatcher.dispatch(new A(), new B(), new C()); with ... notation, see comment below
接口:
public interface Result { ... }
public interface SuperABC extends Callable<Result> {}
類示例:
public class A implements SuperABC {
public Result call() throws Exception { ... }
}
調度方式:
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.