我是否需要同步invokeAll调用的结果?

时间:2009-09-01 06:58:44

标签: java concurrency synchronization

我正在增强现有算法,该算法由多个独立步骤组成,以使用并发任务。每个任务都将创建多个对象来保存其结果。最后,我想列出从控制方法返回的所有结果。目前,我的代码看起来像那样

private final ExecutorService pool = ...;

// A single task to be performed concurrently with other tasks.
private class WorkHorse implements Callable<Void> {
    private final Collection<X> collect;

    public WorkHorse(Collection<X> collect, ...) {
        this.collect = collect;
    }

    public Void call() {
        for (...) {
            // do work

            synchronized (this.collect) {
                this.collect.add(result);
            }
        }
        return null;
    }
}

// Uses multiple concurrent tasks to compute its result list.
public Collection<X> getResults() {
    // this list is supposed to hold the results
    final Collection<X> collect = new LinkedList<X>();

    final List<WorkHorse> tasks = Arrays.asList(  
        new WorkHorse(collect, ...), new WorkHorse(collect, ...), ...);
    this.pool.invokeAll(tasks);

    // ## A ##
    synchronized (collect) {
        return collect;
    }
}

我是否真的需要{#1}}“## A ##”来强制执行与工作任务中的修改操作之前发生的关系?或者我可以依赖所有写操作在synchronized返回后发生并且对控制线程可见吗?有什么理由,为什么我不应该从自己的invokeAll块中返回结果集合?

2 个答案:

答案 0 :(得分:3)

不,你不需要那个。 invokeAll的文档指出,所有工作都应在返回时完成。所以当你到达return语句时,不应该有进一步的访问权。

答案 1 :(得分:0)

如果您有第二个synchronized,则不需要第二个invokeAll()。正如Zed所说,add()将阻止所有任务完成。同时,synchronized周围的同步将确保对集合的更改对所有线程都可见,包括原始调用线程。

至于你是否需要第一个(你没有问过) - 我试图删除两个LinkedList块并且实际上无法让它失败,但是它可能是更安全的赌注。根据{{​​1}}的javadoc:

  

如果多个线程访问a   LinkedList同时发生,至少是   其中一个线程修改了列表   在结构上,必须   外部同步。

其他“第二代”Collection实施也有类似的警告。

请注意,对集合本身进行同步并没有什么神奇之处。您可以在外部类中声明一个单独的互斥锁(任何旧的Object都可以),或者在外部类实例上进行同步,这也可以正常工作,只要所有WorkHorse同步同样的事情。