我想知道是否有办法监控线程的生命,但我会解释我正在做的事情的背景,所以也许有更好的方法来做到这一点。
基本上我有x个线程正在处理队列并处理它,如果一个线程得到一个可接受的结果它进入一个解决方案队列,否则数据被丢弃或进一步处理。
我的问题是在我的主线程中我有一个类似while(!solutions_results.isEmpty())
并且它保存数据(现在它的打印到文件但后来可能是数据库)。显而易见的问题是,一旦它清除了解决方案队列完成并完成工作,即使其他线程仍在将数据放入队列中。
我不确定解决这个问题的最佳方法(也许有一个只保存解决方案队列的专用线程?)但我在想,如果我能以某种方式监视其他线程的生命,那么就没有机会了更多数据进入解决方案队列。
如果有更好的方法,请告诉我,否则有一种方法可以告诉其他线程完成后(我不能等待执行程序在运行此过程之前完全完成,因为它可能变得非常大并且不希望它只是坐在内存中,理想情况下想要处理它,因为它接近但它不依赖于时间)?
答案 0 :(得分:2)
如果您使用ExecutorService
来运行线程作业,那么您可以使用awaitTermination()
方法知道所有线程何时完成:
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(yourSolutionsRunnable);
pool.submit(yourSolutionsRunnable);
...
// once you've submitted your last job you can do
pool.shutdown();
然后您可以等待提交的所有作业完成:
pool.waitTermination(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
如果您的线程在提交解决方案后需要继续运行,这将变得更加复杂。如果您编辑问题并使其更加明显,我将编辑我的答案。
修改强>
哦,我发现你想要一路上处理一些结果,但是在完成所有线程之前不要停止。
您可以使用pool.isTerminated()
测试来告诉您所有工作是否已完成。所以你的循环看起来像是:
// this is the main thread so waiting for solutions in a while(true) loop is ok
while (true) {
// are all the workers done?
if (pool.isTerminated()) {
// if there are results process one last time
if (!solutions_results.isEmpty()) {
processTheSolutions();
}
break;
} else {
if (solutions_results.isEmpty()) {
// wait a bit to not spin, you could also use a wait/notify here
Thread.sleep(1000);
} else {
processTheSolutions();
}
}
}
修改强>
您还可以拥有两个线程池,一个用于生成解决方案,另一个用于处理。然后,您的主线程可以等待工作池清空,然后等待解决方案处理池。工作池将解决方案(如果有)提交到解决方案池中。根据需要,您可以在解决方案处理池中放置一个或多个线程。
ExecutorService workerPool = Executors.newFixedThreadPool(10);
final ExecutorService solutionsPool = Executors.newFixedThreadPool(1);
solutionsPool.submit(workerThatPutsSolutionsIntoSolutionsPool);
...
// once you've submitted your last worker you can do
workerPool.shutdown();
workerPool.waitTermination(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
// once the workers have finished you shutdown the solutions pool
solutionsPool.shutdown();
// and then wait for it to finish
solutionsPool.waitTermination(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
答案 1 :(得分:0)
我不太了解您正在处理的行为要求,但是如果您希望主线程阻塞直到您的所有子线程都完成,那么您应该看一下join
方法。 Thread
上课。
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()
只需在主线程中运行一个循环,在每个子线程上调用join方法,当它退出循环时,您可以确保所有线程都已完成工作。
答案 2 :(得分:0)
只需保留活动主题列表。如果同时添加/删除线程,您希望它同步以防止被删除。或者使用类似java.util.concurrent.ConcurrentLinkedQueue的东西,它可以处理多个线程本身。启动它时,将每个线程添加到列表中。每个线程都应该在它停止之前从列表中删除它自己。当列表为空时,所有线程都已完成。
编辑:时机非常重要。 首先,主线程必须将工作线程放入列表中。如果他们将自己放入列表中,主线程可以在某些线程已从列表中删除自己的时候检查列表,并且所有其余的(尽管已经启动)尚未开始执行 - 因此尚未将其自身放入名单。然后它会认为一切都是在没有的时候完成的。 其次,主线程必须在启动它之前将每个工作线程放在列表上。否则,线程可能会完成并尝试从列表中删除自己,然后主线程将其添加到列表中。然后列表永远不会变空,程序永远不会完成。
答案 3 :(得分:0)
也许java.util.concurrent.ExecutorCompletionService在这里很有用。
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class monitor_life_of_threads
{
/**
* First, convert all of your threads to instances of Callable (easy to do), and have them each return an instance some class (I'm using Integer below just as
* an example).
* This will help simplify things.
*/
public static void main ( String args[] )
{
final monitor_life_of_threads worker = new monitor_life_of_threads();
worker.executeCallablesAndUseResults ();
System.exit ( 0 );
}
private void executeCallablesAndUseResults ()
{
List < Callable < Result >> list = new ArrayList <> ();
populateInputList ( list );
try
{
doWork ( list );
}
catch ( InterruptedException e )
{
e.printStackTrace ();
}
catch ( ExecutionException e )
{
e.printStackTrace ();
}
catch ( CancellationException e )
{
/*
* Could be called if a Callable throws an InterruptedException, and if it's not caught, it can cause Future.get to hang.
*/
e.printStackTrace ();
}
catch ( Exception defaultException )
{
defaultException.printStackTrace ();
}
}
private void doWork ( Collection < Callable < Result >> callables ) throws InterruptedException, ExecutionException
{
ExecutorService executorService = Executors.newCachedThreadPool ();
CompletionService < Result > ecs = new ExecutorCompletionService < > ( executorService );
for ( Callable < Result > callable : callables )
ecs.submit ( callable );
for ( int i = 0, n = callables.size (); i < n; ++i )
{
Result r = ecs.take ().get ();
if ( r != null )
use ( r ); // This way you don't need a second queue.
}
executorService.shutdown ();
}
private void use ( Result result )
{
// Write result to database, output file, etc.
System.out.println ( "result = " + result );
}
private List < Callable < Result >> populateInputList ( List < Callable < Result >> list )
{
list.add ( new Callable < Result > () {
@Override
public Result call () throws Exception
{
// Do some number crunching, then return a 5.
return new Result ( 5 );
}
} );
list.add ( new Callable < Result > () {
@Override
public Result call () throws Exception
{
// Do some number crunching, then return an 8.
return new Result ( 8 );
}
} );
list.add ( new Callable < Result > () {
@Override
public Result call () throws Exception
{
// Do some number crunching, but fail and so return null.
return null;
}
} );
return list;
}
}
class Result
{
private Integer i;
Result ( Integer i)
{
this.i = i;
}
public String toString ()
{
return Integer.toString ( i );
}
}