1

假设我在一个程序中有多个 Runnable 实例,全部由一个 Executor 实例调度。此外,假设我在某些时候需要等待这些可运行文件的一个子集完成,然后再继续。

我可以这样做的一种方法是:

public abstract class Joinable implements Runnable {

    private final Semaphore finishedLock = new Semaphore(1);

    @Override
    public final void run() {
        try {
            finishedLock.acquireUninterruptibly();
            doWork();
        } finally {
            finishedLock.release();
        }
    }

    public abstract void doWork();

    public void join() {
        finishedLock.acquireUninterruptibly();
    }
}

然后实现类可以简单地覆盖 doWork(),而不是 run(),以便定义在执行期间应该做什么。

加入过程将如下所示:

void doStuff() {

    Executor executor = Executors.newCachedThreadPool();

    List<Joinable> joinables = new LinkedList<Joinable>();
    // Fill joinables with implementors of Joinable...

    List<Runnable> others = new LinkedList<Runnable>();
    // Fill others with implementors of Runnable...

    for(Joinable joinable : joinables) 
        executor.execute(joinable);

    for(Runnable runnable : others) 
        executor.execute(runnable);

    for(Joinable joinable : joinables) 
        joinable.join();

    // Continue, no matter what the threads in others are up to.
}

这是解决这个问题的好方法(它甚至安全吗?),还是有更好的方法?

4

2 回答 2

2

您当前的解决方案不是线程安全的。无法保证执行人会runJoinable您致电之前致电您join。因此,在某些情况下,您的主线程将在您之前获得锁Joinable

CountDownLatch如果您知道 joinables 的总数,则可能的解决方案是使用 a ,然后N创建 aCountDownLatch(N)并将其传递给每个实例。当每个可连接对象完成后,调用countDown(). 您的主线程调用await()闩锁。 await()在锁存器计数为 0 之前不会返回。

于 2013-03-26T21:02:05.777 回答
1

这是解决这个问题的好方法吗(它甚至安全吗?)

这不太对。您无法加入Runnable您正在执行的ExecutorService. 如果您想使用列表,请执行以下操作:

List<Future<?>> futures = new ArrayList<Future<?>>();
for(Joinable joinable : joinables) {
   // this submit returns a `Future`.
   futures.add(executor.submit(joinable));
}
// submit others to the executor _without_ adding to the futures list

for (Future<?> future : futures) {
    // this can throw ExecutionException which wraps exception thrown by task
    future.get();
}

还是有更好的?

如果您正在等待所有任务完成,那么您可以使用该ExecutorService.awaitTermination(long timeout, TimeUnit unit)方法。例如:

executor.awaitTerminate(Long.MAX_VALUE, TimeUnit.MILLISECONDS);

但是,如果您正在等待任务的子集,我看不出有更好的方法来做到这一点。

于 2013-03-26T20:57:30.307 回答