如何监控线程的寿命?

时间:2012-04-13 17:03:11

标签: java multithreading

我想知道是否有办法监控线程的生命,但我会解释我正在做的事情的背景,所以也许有更好的方法来做到这一点。

基本上我有x个线程正在处理队列并处理它,如果一个线程得到一个可接受的结果它进入一个解决方案队列,否则数据被丢弃或进一步处理。

我的问题是在我的主线程中我有一个类似while(!solutions_results.isEmpty())并且它保存数据(现在它的打印到文件但后来可能是数据库)。显而易见的问题是,一旦它清除了解决方案队列完成并完成工作,即使其他线程仍在将数据放入队列中。

我不确定解决这个问题的最佳方法(也许有一个只保存解决方案队列的专用线程?)但我在想,如果我能以某种方式监视其他线程的生命,那么就没有机会了更多数据进入解决方案队列。

如果有更好的方法,请告诉我,否则有一种方法可以告诉其他线程完成后(我不能等待执行程序在运行此过程之前完全完成,因为它可能变得非常大并且不希望它只是坐在内存中,理想情况下想要处理它,因为它接近但它不依赖于时间)?

4 个答案:

答案 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 );
    }
}