Нужно ли выполнять синхронизацию по результату вызова invokeAll? - PullRequest
4 голосов
/ 01 сентября 2009

Я улучшаю существующий алгоритм, который состоит из нескольких независимых шагов для использования параллельных задач. Каждая из задач создаст несколько объектов для хранения своих результатов. В конце я хотел бы получить список всех результатов, возвращаемых из метода управления. На данный момент мой код выглядит примерно так

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;
    }
}

Нужно ли мне на самом деле synchronized в "## A ##", чтобы навязывать связь "произойдет до" с операциями изменения в рабочих задачах? Или я могу рассчитывать на то, что все операции записи произошли после возврата invokeAll и будут видны управляющему потоку? И есть ли причина, по которой я не должен возвращать сбор результатов из своего собственного блока synchronized?

Ответы [ 2 ]

3 голосов
/ 01 сентября 2009

Нет, тебе это не нужно. Документация invokeAll гласит, что все работы должны быть выполнены, когда он вернется. Поэтому, когда вы достигнете оператора return, доступа к сбору больше не будет.

0 голосов
/ 01 сентября 2009

Вам не нужен второй synchronized, если у вас там есть первый. Как отмечает Зед, invokeAll() будет блокироваться, пока все задачи не будут выполнены. Между тем, синхронизация вокруг add() обеспечит видимость изменений в коллекции для всех потоков, включая исходный вызывающий поток.

Что касается того, нужен ли вам первый один (о котором вы не спрашивали) - я попытался удалить оба блока synchronized и не смог его вызвать, но он был там вероятно, более безопасная ставка. Согласно javadoc для LinkedList:

Если несколько потоков обращаются к LinkedList одновременно и по крайней мере один из потоков изменяет список структурно, это должно быть внешняя синхронизация.

Другие реализации Collection 2-го поколения имеют аналогичные предупреждения.

Заметьте, кстати, что нет ничего волшебного в синхронизации самой коллекции. Вы можете объявить отдельный мьютекс (любой старый Object) во внешнем классе или выполнить синхронизацию с экземпляром внешнего класса, и это будет работать так же хорошо, если все WorkHorse синхронизируются на одном и том же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...