我遇到的问题是我的方法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>
等。我在案例陈述中调用submit()
所以我无法访问案例外的返回Future<T>
。现在我可以进行if, else
而不是for
循环,但我想弄清楚是否有更好的方法来实现这一目标。
答案 0 :(得分:3)
我会这样做 -
I
。A
,B
,C
和D
实施I
。valueOf
和对象overriding
删除案例陈述。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;
}
答案 1 :(得分:1)
最好从未来拆分get,因此在枚举中添加一个Callable作为参数。然后,枚举瞬间可以创造一个未来。 不幸的是,对于通用类型,需要存储生成的类,并用于正确输入。
'use strict';
/**
* AcNewDatabaseFormat Enumeration
* Used with the NewCurrentDatabase method to specify the database format of the newly created database.
*/
var acModule = 5,
dbText = 10,
acNewDatabaseFormat = {
UserDefault: 0,
Access2000: 9,
Access2002: 10,
Access12: 12
};
var fs = new ActiveXObject('Scripting.FileSystemObject');
var access = new ActiveXObject('Access.Application');
var basePath = fs.GetParentFolderName(WScript.ScriptFullName);
var db, prop, vcsFolder, fCur, module;
//Create DB and set up some superficial things.
access.NewCurrentDatabase(basePath + '\\ImportTest.accdb', acNewDatabaseFormat.Access12);
db = access.CurrentDb();
prop = db.CreateProperty('AppTitle', dbText, 'IG IMI Database');
db.Properties.Append(prop);
prop = db.CreateProperty('StartUpForm', dbText, 'Main Switchboard');
db.Properties.Append(prop);
db.Properties('UseMDIMode') = 1;
//Add MSAccess-VCS modules
vcsFolder = fs.GetFolder(basePath + '\\MSAccess-VCS');
fCur = new Enumerator(vcsFolder.files);
for (; !fCur.atEnd(); fCur.moveNext()) {
module = fCur.item().Name.replace('.bas', '');
access.LoadFromText(acModule, module, fCur.item());
}
access.Run('ImportAllSource');
access.Quit();
答案 2 :(得分:1)
如果这些是您提交给Callables
的唯一ExecutorService
,那么您可以在提交作业后cachePool
致电awaitTermination(可以Runnable
1}}代替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。使用零许可初始化Semaphore
,每个任务在{I}完成时release
允许,dispatch
方法阻塞semaphore.acquire(methodNames.size())
,等待所有任务调用{{1} (并因此完成)。请注意release
中的try-finally
块,否则如果Runnable
抛出异常,则不会调用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());
}
都可以将其结果存储在一个共享{ {3}}或其他一些线程安全的数据结构(或每个返回类型的一个数据结构等),然后Runnable
可以在dispatch
或semaphore.acquire
方法解除阻塞时处理这些结果。
答案 3 :(得分:0)
您需要通过多个步骤来处理结果:
在这种情况下,您可以在签名中使用Future<?>
,因为只是等待您不必了解结果类型。所以你可以创建一个方法void waitForAll(List< Future<?> > futures)
。
为此你需要某种知道Future
将提供的类型的句柄。由于Java的类型擦除,这个句柄必须以某种方式存储Class<T>
。因此,最简单的句柄是相应的Future<T>
本身(T
是示例中的A
和B
之一)。
因此,您可以使用Map<Class<?>, Future<?>)
类型的其他get方法将期货存储在MultiMap
(或Future<T> get<T>(Class<T> handle)
)中。
您可以使用此句柄替换枚举MethodNames
。
Map
/ MultiMap
,例如:通过取消对dispatch
方法的多次调用。Map
/ MultiMap
获取相应的结果答案 4 :(得分:0)
你正在尝试做一些像fork-join或map-reduce之类的东西。您可能会找到一种既定机制来完成此任务,而不是重新发明轮子。
无论如何回到等待所有方法完成并继续前进的具体问题:
正如你所提到的,你不应该失去指向未来的指针。因此,创建一个结构结果,这可以容纳所有未来。当前线程中的结果 等待。在结果中运行另一个线程,它将监视期货,并在返回所有方法时通知。
当结果通知时,您将在当前主题中向前移动,并且结果对象保留所有返回的数据。
答案 5 :(得分:0)
简单(有限)解决方案:如果您可以为返回值定义接口/超类(类似于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;
}