具有不同返回类型的可变callables

时间:2015-06-19 19:25:29

标签: java multithreading

我遇到的问题是我的方法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循环,但我想弄清楚是否有更好的方法来实现这一目标。

6 个答案:

答案 0 :(得分:3)

我会这样做 -

  • 创建一个界面,让我们说I
  • 让课程ABCD实施I
  • 使用枚举valueOf和对象overriding删除案例陈述。
  • 使用多态并从所有方法返回I
  • 以下是代码(不包括ABCDI),因为它们是普通的类和接口 - 没有做得多。

以下是代码:

  

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抛出异常,则不会调用Runnablerelease方法将永久阻止。

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可以在dispatchsemaphore.acquire方法解除阻塞时处理这些结果。

答案 3 :(得分:0)

成分

您需要通过多个步骤来处理结果:

等待多个期货

在这种情况下,您可以在签名中使用Future<?>,因为只是等待您不必了解结果类型。所以你可以创建一个方法void waitForAll(List< Future<?> > futures)

以安全的方式从未知的未来获得结果

为此你需要某种知道Future将提供的类型的句柄。由于Java的类型擦除,这个句柄必须以某种方式存储Class<T>。因此,最简单的句柄是相应的Future<T>本身(T是示例中的AB之一)。

因此,您可以使用Map<Class<?>, Future<?>)类型的其他get方法将期货存储在MultiMap(或Future<T> get<T>(Class<T> handle))中。

您可以使用此句柄替换枚举MethodNames

收据:将成分与解决方案相结合

  1. 如上所述创建Map / MultiMap,例如:通过取消对dispatch方法的多次调用。
  2. 将waitAll与地图的值列表
  3. 一起使用
  4. 使用上述get方法从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;
}