📜  Java集合中同步ArrayList和CopyOnWriteArrayList的区别

📅  最后修改于: 2022-05-13 01:54:30.951000             🧑  作者: Mango

Java集合中同步ArrayList和CopyOnWriteArrayList的区别

众所周知,ArrayList 是不同步的,如果多个线程同时尝试修改一个 ArrayList,那么最终的结果将是不确定的。因此,为了在多线程环境中实现线程安全,必须同步 ArrayList。

为了制作 List 对象,我们通常会创建 List 接口的对象,并根据我们的要求制作 List 类,并在最近添加元素并进行访问、更新而无需考虑线程安全。这个概念很简单,同时也有点高级,因为大多数Java开发人员在编写代码时并没有实践这种技术。

在 ArrayList 中实现同步的不同方式

Arraylist 中的同步可以通过两种方式实现:

  1. 使用 Collections 类的 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 sal = new ArrayList();
 
        // 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 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 以及何时使用 Collections 类的 synchronizedList() 方法。这可以通过了解它们之间的差异来理解。 synchronized ArrayList 和 CopyOnWriteArrayList 之间的主要区别在于它们的性能、可伸缩性以及它们如何实现线程安全。

当 Collection.synchronizedList() 已经存在时,为什么 CopyOnWriteArrayList 会存在?

所以答案很简单,因为 SynchronizedList 最初是在多线程环境中使用的,但它有一些限制。它的所有读取和写入方法都在列表对象本身上同步,即,如果一个线程正在执行 add() 方法,它会阻塞其他想要让迭代器访问列表中元素的线程。此外,一次只允许一个线程迭代列表的元素,这是低效的。那是相当僵硬的。因此需要一个更灵活的集合,它允许:

  1. 多个线程同时执行读取操作。
  2. 一个线程执行读操作,另一个线程同时执行写操作。
  3. 只有一个线程可以执行写操作,而其他线程可以同时执行读操作。

为了克服这些问题,最后,在Java 5中,引入了一组名为Concurrent Collections的新集合类,其中包含CopyOnWriteArrayList 。 CopyOnWriteArrayList 类旨在启用此类顺序写入和并发读取功能。

让我们讨论与它们相关的特征,这些特征在它们之间产生了一条细线,如下所示:

1. 线程锁定

Synchronized List锁定整个列表以在读取或写入操作期间提供同步和线程安全,而CopyOnWriteArrayList在这些操作期间不会锁定整个列表。
CopyOnWriteArrayList 类根据其名称工作,即写时复制,它为读取和写入操作执行不同的操作。对于每个写入操作(添加、设置、删除等),它都会为列表中的元素创建一个新副本。对于读取操作(get、iterator、listIterator 等),它适用于不同的副本。因此在读取操作期间没有额外的开销,并且它的读取操作比 Collections.SynchronizedList() 更快。因此, COWAL 比同步列表更适合读取操作。

2.写操作

对于 ArrayList 中的写操作, COWAL 写操作比 Collections.synchronizedList() 慢,因为它使用了 Re-entrantLock。 write 方法将始终创建现有数组的副本并对副本进行修改,然后最终更新数组的 volatile 引用以指向这个新数组。因此,它在写入操作期间具有巨大的开销。这就是为什么 CopyOnWriteArrayList 写操作比 Collections.synchronizedList() 慢的原因。

3. 修改期间的行为

Synchronized List 是一个快速失败的迭代器,即当一个线程对其进行迭代时,当列表被修改时它会抛出 ConcurrentModifcationException 而CopyOnWriteArrayList 是一个故障安全迭代器,即即使当一个线程修改列表时它也不会抛出 ConcurrentModifcationException正在迭代它。

4. 工作线程数

只允许一个线程在同步列表上操作,通过锁定影响其性能的完整列表对象,因为其他线程正在等待,而在COWAL的情况下,允许多个线程在 ArrayList 上操作,因为它在单独的克隆上工作复制用于更新/修改操作,使其性能更快

5. 块内迭代

在迭代同步列表时,请确保在同步块内进行迭代,而在 CopyOnWriteArrayList 中,我们可以安全地在同步块外进行迭代。

何时使用 SynchronizedList?

  • 由于在每个更新/修改操作的 CopyOnWriteArrayList 中,都会创建一个新的单独克隆副本,并且 JVM 分配内存并将克隆副本与原始副本合并会产生开销。因此,在这种情况下,SynchronizedList 是一个更好的选择。当 Arraylist 的大小很大时。
  • 当 Arraylist 的大小很大时。

何时使用 CopyOnWriteArrayList?

  • CopyOnWriteArrayList 提供无锁读取,这意味着如果有更多读取器线程并且写入发生率很低,则性能会更好。
  • 当 Arraylist 的大小较小时。

SynchronizedList 与 CopyOnWriteArrayList

SynchronizedArrayList CopyOnWriteArrayList 
It was introduced in Java version 1.2It was introduced in Java version 1.5
It should be used when there are more write operations over-read operations.It should be used when there are more read operations than write operations.
The iterator used is fail-fast.The iterator used is fail-safe.
The iteration of List has to be there inside the synchronized block.  The iteration of the list can be outside the synchronized block.
The whole ArrayList is locked by Synchronized Arraylsit for thread safety during read and write operations. The whole ArrayList is locked by SynchronizedArrayList for thread safety during the write operations only.
It is preferred when ArrayList is larger.It is preferred when ArrayList is smaller.