Разница между Synchronized ArrayList и CopyOnWriteArrayList в коллекции Java

Опубликовано: 15 Сентября, 2022

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

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

Note: Synchronized ArrayList is synchronized collection while CopyOnWriteArrayList is an concurrent collection as it is made with keeping concurrency.

Различные способы достижения синхронизации в ArrayList

Синхронизация в Arraylist может быть достигнута двумя способами:

  1. Использование метода synchronizedList() класса коллекций
  2. Использование CopyOnWriteArrayList (COWAL)

Пример

Java




// Java Program to Illustrate Synchronized ArrayList
// Using synchronizedList() Method
 
// Importing required classes
import java.util.* ;
 
// Main class
// SynchronizedArrayList
class GFG {
 
    // Main driver method
    public static void main(String[] args) {
 
        // Creating an empty ArrayList of string type
        // By default, non - synchronized List
        List<String> sal = new ArrayList<String>();
 
        // Adding elements to above List
        // using add() method
        sal.add("Geeks");
        sal.add("for");
        sal.add("Geeks");
        sal.add("Computer");
        sal.add("Science");
        sal.add("Portal");
 
        // Printing the above non-synchronised List
        System.out.println(sal);
 
        // Synchronizing above List
        // using SynchronizedList() method
        Collections.synchronizedList(sal);
 
        // Synchronized block to
        // avoid non-deterministic behavior
        synchronized (sal) {
 
            // Using iterators to iterate over elements
            Iterator<String> itrobj = sal.iterator();
 
            // Holds true till there is single element remaining
            while (itrobj.hasNext()) {
                // Printing elements
                // using next() method
                System.out.println(itrobj.next());
            }
        }
    }
}

Выход:

Поскольку оба способа используются для обеспечения потокобезопасности в Arraylist. Возникает вопрос, когда использовать COWAL, а когда использовать метод synchronizedList() класса Collections. Это можно понять, разобравшись в различиях между ними. Основное различие между синхронизированным ArrayList и CopyOnWriteArrayList связано с их производительностью, масштабируемостью и тем, как они обеспечивают безопасность потоков.

Почему появился CopyOnWriteArrayList, когда Collection.synchronizedList() уже существовал?

Так что ответ довольно прост, потому что изначально SynchronizedList использовался в многопоточной среде, но имел некоторые ограничения. Все его методы чтения и записи были синхронизированы с самим объектом списка, т.е. если поток выполняет метод add(), он блокирует другие потоки, которые хотят, чтобы итератор получил доступ к элементам в списке. Кроме того, только одному потоку разрешалось перебирать элементы списка за раз, что было неэффективно. Это было довольно жестко. Таким образом, требовалась более гибкая коллекция, позволяющая:

  1. Несколько потоков, выполняющих операции чтения одновременно.
  2. Один поток выполняет операцию чтения, а другой одновременно выполняет операцию записи.
  3. Только один поток может выполнять операции записи, в то время как другие потоки могут одновременно выполнять операции чтения.

Наконец, чтобы преодолеть эти проблемы, в Java 5 был введен новый набор классов коллекций, называемых параллельными коллекциями , в которых был CopyOnWriteArrayList . Класс CopyOnWriteArrayList предназначен для включения таких функций последовательной записи и одновременного чтения.

Давайте обсудим характеристики, связанные с ними обоими, которые создают тонкую грань различий между ними, как указано ниже:

1. Блокировка потоков

Synchronized List блокирует весь список, чтобы обеспечить синхронизацию и безопасность потоков во время операций чтения или записи, а CopyOnWriteArrayList не блокирует весь список во время этих операций.
Класс CopyOnWriteArrayList работает в соответствии со своим именем, то есть копирование при записи , которое выполняет различные действия для операций чтения и записи. Для каждой операции записи (добавление, установка, удаление и т. д.) создается новая копия элементов в списке. а для операций чтения (get, iterator, listIterator и т. д.) он работает с другой копией. Таким образом, во время операции чтения нет дополнительных накладных расходов, и операция чтения выполняется быстрее, чем Collections.SynchronizedList(). Таким образом, COWAL лучше подходит для операций чтения, чем Synchronized List.

2. Операции записи

Для операции записи в ArrayList операции записи COWAL выполняются медленнее, чем Collections.synchronizedList(), поскольку он использует Re-entrantLock. Метод записи всегда создает копию существующего массива и выполняет модификацию копии, а затем, наконец, обновляет изменчивую ссылку массива, чтобы она указывала на этот новый массив. Следовательно, во время операции записи возникают огромные накладные расходы. Вот почему операции записи CopyOnWriteArrayList выполняются медленнее, чем Collections.synchronizedList().

3. Поведение во время модификации

Синхронизированный список — отказоустойчивый итератор , т. е. он будет генерировать ConcurrentModifcationException, когда список будет изменен, когда один поток выполняет итерацию по нему, тогда как CopyOnWriteArrayList — это отказоустойчивый итератор , т. е. он не будет генерировать ConcurrentModifcationException, даже когда список изменяется, когда один поток перебирает его.

4. Количество работающих потоков

Только одному потоку разрешено работать с синхронизированным списком путем блокировки всего объекта списка, что влияет на его производительность, поскольку другие потоки ожидают, тогда как в случае COWAL нескольким потокам разрешено работать с ArrayList, так как он работает с отдельными клонированными копировать для операций обновления/изменения, что повышает его производительность .

5. Итерация внутри блока

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

Когда использовать SynchronizedList?

  • Поскольку в CopyOnWriteArrayList для каждой операции обновления/изменения создается новая отдельная клонированная копия, и на JVM есть накладные расходы на выделение памяти и объединение клонированной копии с исходной копией. Таким образом, в этом случае SynchronizedList является лучшим вариантом. Когда размер Arraylist велик.
  • Когда размер Arraylist большой.

Когда использовать CopyOnWriteArrayList?

  • CopyOnWriteArrayList обеспечивает чтение без блокировки, что означает гораздо лучшую производительность, если есть больше потоков чтения и запись происходит довольно медленно.
  • Когда размер Arraylist мал.

SynchronizedList против CopyOnWriteArrayList

СинхронизированныйArrayList КопиОнВритеАррайлист
Он был представлен в версии Java 1.2. Он был представлен в версии Java 1.5.
Его следует использовать, когда операций записи больше, чем операций чтения. Его следует использовать, когда операций чтения больше, чем операций записи.
Используемый итератор отказоустойчив. Используемый итератор отказоустойчив.
Итерация List должна быть внутри синхронизированного блока. Итерация списка может быть за пределами синхронизированного блока.
Весь список ArrayList блокируется Synchronized Arraylist для обеспечения безопасности потоков во время операций чтения и записи. Весь ArrayList заблокирован SynchronizedArrayList для безопасности потоков только во время операций записи.
Предпочтительнее, когда ArrayList больше. Предпочтительнее, когда ArrayList меньше.