Параллельное добавление потоков в ArrayList одновременно - что происходит? - PullRequest
47 голосов
/ 26 апреля 2010

У нас есть несколько потоков, вызывающих add(obj) на ArrayList.

Моя теория состоит в том, что когда add вызывается одновременно двумя потоками, то только один из двух добавляемых объектов действительно добавляется в ArrayList. Это правдоподобно?

Если это так, как вы справляетесь с этим? Используйте синхронизированную коллекцию, например Vector?

Ответы [ 9 ]

48 голосов
/ 26 апреля 2010

Не существует гарантированного поведения того, что происходит, когда add вызывается одновременно двумя потоками в ArrayList. Однако, по моему опыту, оба объекта были добавлены нормально. Большинство проблем безопасности потоков, связанных со списками, касаются итерации при добавлении / удалении. Несмотря на это, я настоятельно рекомендую не использовать ванильный ArrayList с несколькими потоками и одновременным доступом.

Вектор раньше был стандартом для одновременных списков, но теперь стандартом является использование синхронизированного списка коллекций .

Также я настоятельно рекомендую Java Concurrency in Practice Гетца и др., Если вы собираетесь проводить какое-то время с потоками в Java. Книга освещает эту проблему гораздо более подробно.

15 голосов
/ 26 апреля 2010

Может произойти любое количество вещей. Вы можете правильно добавить оба объекта. Вы можете добавить только один из объектов. Вы можете получить исключение ArrayIndexOutOfBounds, потому что размер базового массива не был правильно настроен. Или могут произойти другие вещи. Достаточно сказать, что вы не можете полагаться на любое происходящее поведение.

В качестве альтернативы вы можете использовать Vector, вы можете использовать Collections.synchronizedList, вы можете использовать CopyOnWriteArrayList, или вы можете использовать отдельную блокировку. Все зависит от того, что вы еще делаете, и какой контроль вы имеете над доступом к коллекции.

8 голосов
/ 26 апреля 2010

Вы также можете получить null, ArrayOutOfBoundsException или что-то еще до реализации. * * * * * * * * * * * * В производственных системах было обнаружено, что HashMap с входят в бесконечный цикл Вам не нужно знать, что может пойти не так, просто не делайте этого.

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

5 голосов
/ 18 января 2015

Я придумал следующий код для имитации сценария реального мира.

100 задач выполняются параллельно, и они обновляют свое завершенное состояние для основной программы. Я использую CountDownLatch для ожидания завершения задачи.

import java.util.concurrent.*;
import java.util.*;

public class Runner {

    // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>())
    public List<Integer> completed = new ArrayList<Integer>();

    /**
     * @param args
     */
    public static void main(String[] args) {
        Runner r = new Runner();
        ExecutorService exe = Executors.newFixedThreadPool(30);
        int tasks = 100;
        CountDownLatch latch = new CountDownLatch(tasks);
        for (int i = 0; i < tasks; i++) {
            exe.submit(r.new Task(i, latch));
        }
        try {
            latch.await();
            System.out.println("Summary:");
            System.out.println("Number of tasks completed: "
                    + r.completed.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        exe.shutdown();
    }

    class Task implements Runnable {

        private int id;
        private CountDownLatch latch;

        public Task(int id, CountDownLatch latch) {
            this.id = id;
            this.latch = latch;
        }

        public void run() {
            Random r = new Random();
            try {
                Thread.sleep(r.nextInt(5000)); //Actual work of the task
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            completed.add(id);
            latch.countDown();
        }
    }
}

Когда я запускал приложение 10 раз и не менее 3-4 раз, программа не выводила правильное количество выполненных задач. В идеале должно быть напечатано 100 (если не происходит исключений). Но в некоторых случаях это была печать 98, 99 и т. Д.

Таким образом, это доказывает, что одновременные обновления ArrayList не дадут правильных результатов.

Если я заменю ArrayList версией Synchronized , программа выдаст правильные результаты.

3 голосов
/ 26 апреля 2010

Поведение, вероятно, не определено, так как ArrayList не является потокобезопасным. Если вы изменяете список, когда итератор работает над ним, вы получите исключение ConcurrentModificationException. Вы можете обернуть ArrayList с помощью Collection.synchronizedList или использовать потокобезопасную коллекцию (их много), или просто поместить вызовы add в синхронизированный блок.

3 голосов
/ 26 апреля 2010

вы можете использовать List l = Collections.synchronizedList(new ArrayList());, если вам нужна поточно-ориентированная версия arrayList.

1 голос
/ 26 апреля 2010

java.util.concurrent имеет многопоточный список массивов. Стандартный ArrayList не является потокобезопасным, и поведение, когда несколько потоков обновляются одновременно, не определено. Также может быть странное поведение с несколькими читателями, когда один или несколько потоков пишут одновременно.

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

Вы можете использовать вместо ArrayList();:

Collections.synchronizedList( new ArrayList() );

или

new Vector();

synchronizedList с моей точки зрения предпочтительнее, потому что это:

  • быстрее на 50-100%
  • может работать с уже существующим ArrayList's
0 голосов
/ 26 апреля 2010

http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html

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

Поскольку внутренняя синхронизация отсутствует, то, что вы теоретизируете, неправдоподобно.

Итак, все идет синхронно с неприятными и непредсказуемыми результатами.

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