我正在改变程序在幕后工作的方式。在当前版本中的工作方式如下:
public void threadWork( List<MyCallable> workQueue )
{
ExecutorService pool = Executors.newFixedThreadPool(someConst);
List<Future<myOutput>> returnValues = new ArrayList<Future<myOutput>>();
List<myOutput> finishedStuff = new ArrayList<myOutput>();
for( int i = 0; i < workQueue.size(); i++ )
{
returnValues.add( pool.submit( workQueue.get(i) ) );
}
while( !returnValues.isEmpty() )
{
try
{
// Future.get() waits for a value from the callable
finishedStuff.add( returnValues.remove(0).get(0) );
}
catch(Throwable iknowthisisbaditisjustanexample){}
}
doLotsOfThings(finsihedStuff);
}
但是新系统将使用私有内部Runnable来调用将数据写入全局变量的synchronized方法。我的基本设置是:
public void threadReports( List<String> workQueue )
{
ExecutorService pool = Executors.newFixedThreadPool(someConst);
List<MyRunnable> runnables = new ArrayList<MyRunnable>()
for ( int i = 0; i < modules.size(); i++ )
{
runnables.add( new MyRunnable( workQueue.get(i) );
pool.submit(threads.get(i));
}
while( !runnables.isEmpty() )
{
try
{
runnables.remove(0).wait(); // I realized that this wouldn't work
}
catch(Throwable iknowthisisbaditisjustanexample){}
}
doLotsOfThings(finsihedStuff); // finishedStuff is the global the Runnables write to
}
如果您在尝试第二段代码时阅读我的评论,您会注意到我不知道如何使用wait()。我原以为它基本上就像thread.join()但是在看完文档后我发现它不是。
我可以根据需要更改某些结构,但是使用runnables,让runnables写入全局变量以及使用线程池的基本系统是需求。
在doLotsOfThings()之前,如何等待线程池完全完成?
答案 0 :(得分:2)
您应该致电ExecutorService.shutdown(),然后致电ExecutorService.awaitTermination。
...
pool.shutdown();
if (pool.awaitTermination(<long>,<TimeUnit>)) {
// finished before timeout
doLotsOfThings(finsihedStuff);
} else {
// Timeout occured.
}
答案 1 :(得分:2)
试试这个:
pool.shutdown();
pool.awaitTermination(WHATEVER_TIMEOUT, TimeUnit.SECONDS);
答案 2 :(得分:1)
public void threadReports( List<String> workQueue )
{
ExecutorService pool = Executors.newFixedThreadPool(someConst);
Set<Future<?>> futures = new HashSet<Future<?>>();
for ( int i = 0; i < modules.size(); i++ )
{
futures.add(pool.submit(threads.get(i)));
}
while( !futures.isEmpty() )
{
Set<Future<?>> removed = new Set<Future<?>>();
for(Future<?> f : futures) {
f.get(100, TimeUnit.MILLISECONDS);
if(f.isDone()) removed.add(f);
}
for(Future<?> f : removed) futures.remove(f);
}
doLotsOfThings(finsihedStuff); // finishedStuff is the global the Runnables write to
}
答案 3 :(得分:1)
答案 4 :(得分:1)
shutdown
是ExecutorService
的生命周期方法,并在调用后使执行程序无法使用。在方法中创建和销毁ThreadPools与创建/销毁线程一样糟糕:它几乎违背了使用线程池的目的,即通过启用透明重用来减少线程创建的开销。
如果可能,您应该将ExecutorService生命周期与应用程序保持同步。 - 首次需要时创建,在应用关闭时关闭。
为了实现执行大量任务并等待它们的目标,ExecutorService
提供方法invokeAll(Collection<? extends Callable<T>> tasks)
(如果要等待特定时间段,则提供带超时的版本。)
使用此方法和上述一些要点,相关代码变为:
public void threadReports( List<String> workQueue ) {
List<MyRunnable> runnables = new ArrayList<MyRunnable>(workQueue.size());
for (String work:workQueue) {
runnables.add(new MyRunnable(work));
}
// Executor is obtained from some applicationContext that takes care of lifecycle mgnt
// invokeAll(...) will block and return when all callables are executed
List<Future<MyRunnable>> results = applicationContext.getExecutor().invokeAll(runnables);
// I wouldn't use a global variable unless you have a VERY GOOD reason for that.
// b/c all the threads of the pool doing work will be contending for the lock on that variable.
// doLotsOfThings(finishedStuff);
// Note that the List of Futures holds the individual results of each execution.
// That said, the preferred way to harvest your results would be:
doLotsOfThings(results);
}
PS:不确定为什么threadReports
是void
。它可以/应该返回doLotsOfThings
的计算以实现更实用的设计。