我正在开展一个项目,我将在其中使用不同的Bundles。我们举一个例子,假设我有5个Bundle,每个bundle都有一个方法名process
。
现在,我正在按顺序逐个调用所有这5个包的process
方法,然后我正在写入数据库。但这就是我不想要的。
以下是我要找的东西 -
process
方法,然后写入数据库。我不确定这样做的正确方法是什么?我应该有五个帖吗?每个捆绑一个线程?但是在那种情况下会发生什么,假设我有50个捆绑,那么我将有50个线程?我希望这个问题很清楚。
下面是我到目前为止的代码,它逐个地逐个调用process
方法。
public void callBundles(final Map<String, Object> eventData) {
final Map<String, String> outputs = (Map<String, String>)eventData.get(Constants.HOLDER);
for (final BundleRegistration.BundlesHolderEntry entry : BundleRegistration.getInstance()) {
// calling the process method of a bundle
final Map<String, String> response = entry.getPlugin().process(outputs);
// then write to the database.
System.out.println(response);
}
}
我不确定最好和最有效的方法是什么?而且我不想按顺序写。因为,将来我可能会有超过5个捆绑包。
有人能为我提供一个如何做到这一点的例子吗?我试过这样做但不知怎的,这不是我想要的方式。
任何帮助将不胜感激。感谢。
更新: -
这就是我提出的 -
public void callBundles(final Map<String, Object> eventData) {
// Three threads: one thread for the database writer, five threads for the plugin processors
final ExecutorService executor = Executors.newFixedThreadPool(5);
final BlockingQueue<Map<String, String>> queue = new LinkedBlockingQueue<Map<String, String>>();
@SuppressWarnings("unchecked")
final Map<String, String> outputs = (Map<String, String>)eventData.get(Constants.EVENT_HOLDER);
for (final BundleRegistration.BundlesHolderEntry entry : BundleRegistration.getInstance()) {
executor.submit(new Runnable () {
public void run() {
final Map<String, String> response = entry.getPlugin().process(outputs);
// put the response map in the queue for the database to read
queue.offer(response);
}
});
}
Future<?> future = executor.submit(new Runnable () {
public void run() {
Map<String, String> map;
try {
while(true) {
// blocks until a map is available in the queue, or until interrupted
map = queue.take();
// write map to database
System.out.println(map);
}
} catch (InterruptedException ex) {
// IF we're catching InterruptedException then this means that future.cancel(true)
// was called, which means that the plugin processors are finished;
// process the rest of the queue and then exit
while((map = queue.poll()) != null) {
// write map to database
System.out.println(map);
}
}
}
});
// this interrupts the database thread, which sends it into its catch block
// where it processes the rest of the queue and exits
future.cancel(true); // interrupt database thread
// wait for the threads to finish
try {
executor.awaitTermination(5, TimeUnit.MINUTES);
} catch (InterruptedException e) {
//log error here
}
}
但是我还没有能够添加任何超时功能..而且如果我按原样运行上面的代码,那么它也没有运行..我什么都没有丢失?
有人可以帮我吗?
答案 0 :(得分:0)
这是BASIC示例,部分基于ExecutorService that interrupts tasks after a timeout中提供的解决方案。
您必须找出将其实现到您自己的代码中的最佳方法。仅用它作为指南!
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ExecutorExample {
// This is used to "expire" long running tasks
protected static final ScheduledExecutorService EXPIRE_SERVICE = Executors.newScheduledThreadPool(1);
// This is used to manage the bundles and process them as required
protected static final ExecutorService BUNDLES_SERVICE = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
// A list of the future tasks created by the BUNDLES_SERVICE.
// We need this so we can monitor the progress of the output
List<Future<String>> futureTasks = new ArrayList<>(100);
// This is a list of all the tasks that have either completed
// or begin canceled...we want these so we can determine
// the results...
List<Future<String>> completedTasks = new ArrayList<>(100);
// Add all the Bundles to the BUNDLES_SERVICE
for (int index = 0; index < 100; index++) {
Bundle bundle = new Bundle();
// We need a reference to the future so we can cancel it if we
// need to
Future<String> futureBundle = BUNDLES_SERVICE.submit(bundle);
// Set this bundles future, see Bundle for details
bundle.setFuture(futureBundle);
// Add it to our monitor queue...
futureTasks.add(futureBundle);
}
// Basically we are going to move all completed/canceled bundles
// from the "active" to the completed list and wait until there
// are no more "active" tasks
while (futureTasks.size() > 0) {
try {
// Little bit of a pressure release...
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
// Check all the bundles...
for (Future<String> future : futureTasks) {
// If it has completed or was cancelled, move it to the completed
// list. AKAIK, isDone will return true is isCancelled is true as well,
// but this illustrates the point
if (future.isCancelled() || future.isDone()) {
completedTasks.add(future);
}
}
// Remove all the completed tasks from the future tasks lists
futureTasks.removeAll(completedTasks);
// Some idea of progress...
System.out.println("Still have " + futureTasks.size() + " outstanding tasks...");
}
// Dump the results...
int index = 0;
for (Future<String> future : completedTasks) {
index++;
System.out.print("Task " + index);
if (future.isCancelled()) {
System.out.println(" was canceled");
} else if (future.isDone()) {
try {
System.out.println(" completed with " + future.get());
} catch (Exception ex) {
System.out.println(" failed because of " + ex.getMessage());
}
}
}
System.exit(0);
}
public static class ExpireBundle implements Runnable {
private final Future futureBundle;
public ExpireBundle(Future futureBundle) {
this.futureBundle = futureBundle;
}
@Override
public void run() {
futureBundle.cancel(true);
}
}
public static class Bundle implements Callable<String> {
private volatile Future<String> future;
@Override
public String call() throws Exception {
// This is the tricky bit. In order to cancel a task, we
// need to wait until it runs, but we also need it's future...
// We could use another, single threaded queue to do the job
// but that's getting messy again and it won't provide the information
// we need back to the original calling thread that we are using
// to schedule and monitor the threads...
// We need to have a valid future before we can continue...
while (future == null) {
Thread.sleep(250);
}
// Schedule an expiry call for 5 seconds from NOW...this is important
// I original thought about doing this when I schedule the original
// bundle, but that precluded the fact that some tasks would not
// have started yet...
EXPIRE_SERVICE.schedule(new ExpireBundle(future), 5, TimeUnit.SECONDS);
// Sleep for a random amount of time from 1-10 seconds
Thread.sleep((long) (Math.random() * 9000) + 1000);
return "Happy";
}
protected void setFuture(Future<String> future) {
this.future = future;
}
}
}
另外。我曾想过使用ExecutorService#invokeAll
等待任务完成,但这排除了超时任务的能力。我不想将Future
提供给Callable
,但似乎没有其他解决方案可以让我从提交的Future
获得结果。