📜  Java中ArrayList的同步

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

Java中ArrayList的同步

ArrayList 的实现默认是不同步的。这意味着如果一个线程在结构上修改它并且多个线程同时访问它,它必须在外部同步。结构修改意味着从列表中添加或删除元素或显式调整后备数组的大小。更改现有元素的值不是结构修改。

有两种方法可以创建 Synchronized ArrayList。

1. Collections.synchronizedList() 方法。
2. 使用 CopyOnWriteArrayList。

方法一:使用 Collections.synchronizedList() 方法

要进行串行访问,对后备列表的所有访问都必须通过返回的列表来完成。当迭代它时,用户必须手动同步返回的列表。

public static  List synchronizedList(List list)
  • 接受一个 List,它可能是List接口的实现。例如ArrayList、LinkedList。
  • 返回由指定列表支持的同步(线程安全)列表。
  • 参数列表是要包装在同步列表中的列表。
  • T代表通用
Java
// Java program to demonstrate working of
// Collections.synchronizedList
 
import java.util.*;
 
class GFG
{
    public static void main (String[] args)
    {
        List list =
           Collections.synchronizedList(new ArrayList());
 
        list.add("practice");
        list.add("code");
        list.add("quiz");
 
        synchronized(list)
        {
            // must be in synchronized block
            Iterator it = list.iterator();
 
            while (it.hasNext())
                System.out.println(it.next());
        }
    }
}


Java
// Java program to illustrate the thread-safe ArrayList.
 
import java.io.*;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
 
class GFG
{
    public static void main (String[] args)
    {
        // creating a thread-safe Arraylist.
        CopyOnWriteArrayList threadSafeList
            = new CopyOnWriteArrayList();
 
        // Adding elements to synchronized ArrayList
        threadSafeList.add("geek");
        threadSafeList.add("code");
        threadSafeList.add("practice");
 
        System.out.println("Elements of synchronized ArrayList :");
 
        // Iterating on the synchronized ArrayList using iterator.
        Iterator it = threadSafeList.iterator();
 
        while (it.hasNext())
            System.out.println(it.next());
    }
}


Java
// Java program to illustrate the thread-safe ArrayList
 
import java.io.*;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
 
class GFG
{
    public static void main (String[] args)
    {
        // creating a thread-safe Arraylist.
        CopyOnWriteArrayList threadSafeList =
            new CopyOnWriteArrayList();
 
        // Adding elements to synchronized ArrayList
        threadSafeList.add("geek");
        threadSafeList.add("code");
        threadSafeList.add("practice");
 
        System.out.println("Elements of synchronized ArrayList :");
 
        // Iterating on the synchronized ArrayList using iterator.
        Iterator it = threadSafeList.iterator();
 
        while (it.hasNext())
        {
            String str = it.next();
            it.remove();
        }
    }
}


输出
practice
code
quiz

方法二:使用 CopyOnWriteArrayList

CopyOnWriteArrayList threadSafeList = new CopyOnWriteArrayList();
  • 创建一个空列表。
  • 它实现了 List接口。
  • 它是 ArrayList 的线程安全变体。
  • T代表通用

ArrayList 的线程安全变体,其中所有可变操作(例如,添加、设置、删除..)都是通过创建底层数组的单独副本来实现的。它通过创建 List 的单独副本来实现线程安全,该副本不同于用于提供线程安全的向量或其他集合。

  • 当您不能或不想同步遍历但需要防止并发线程之间的干扰时,它很有用。
  • 这是昂贵的,因为它涉及每个写入操作(例如,添加、设置、删除)的单独数组副本
  • 当你有List并且需要遍历它的元素并且不经常修改它时,它是非常有效的。

即使在创建迭代器后修改了 copyOnWriteArrayList,迭代器也不会抛出ConcurrentModificationException 。迭代器在 ArrayList 的单独副本上进行迭代,同时在 ArrayList 的另一个副本上发生写操作。

Java

// Java program to illustrate the thread-safe ArrayList.
 
import java.io.*;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
 
class GFG
{
    public static void main (String[] args)
    {
        // creating a thread-safe Arraylist.
        CopyOnWriteArrayList threadSafeList
            = new CopyOnWriteArrayList();
 
        // Adding elements to synchronized ArrayList
        threadSafeList.add("geek");
        threadSafeList.add("code");
        threadSafeList.add("practice");
 
        System.out.println("Elements of synchronized ArrayList :");
 
        // Iterating on the synchronized ArrayList using iterator.
        Iterator it = threadSafeList.iterator();
 
        while (it.hasNext())
            System.out.println(it.next());
    }
}
输出
Elements of synchronized ArrayList :
geek
code
practice

如果我们尝试通过迭代器的方法修改 CopyOnWriteArrayList 会发生什么?

如果您尝试通过迭代器自己的方法(例如 add()、set()、remove())修改CopyOnWriteArrayList ,则会抛出 UnsupportedOperationException。

Java

// Java program to illustrate the thread-safe ArrayList
 
import java.io.*;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
 
class GFG
{
    public static void main (String[] args)
    {
        // creating a thread-safe Arraylist.
        CopyOnWriteArrayList threadSafeList =
            new CopyOnWriteArrayList();
 
        // Adding elements to synchronized ArrayList
        threadSafeList.add("geek");
        threadSafeList.add("code");
        threadSafeList.add("practice");
 
        System.out.println("Elements of synchronized ArrayList :");
 
        // Iterating on the synchronized ArrayList using iterator.
        Iterator it = threadSafeList.iterator();
 
        while (it.hasNext())
        {
            String str = it.next();
            it.remove();
        }
    }
}

运行时错误:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove
        (CopyOnWriteArrayList.java:1176)
    at GFG.main(File.java:28)

CopyOnWriteArrayList 的其他构造函数

1. CopyOnWriteArrayList(Collection c) :创建一个包含指定集合元素的列表,按顺序,它们由集合的迭代器返回。

2. CopyOnWriteArrayList(E[] toCopyIn) :创建一个包含给定数组副本的列表。

为什么在向量同步时使用 ArrayList?

  1. 性能: Vector 是同步且线程安全的,因此它比 ArrayList 稍慢。
  2. 功能:向量在每个单独的操作级别同步。通常,程序员喜欢同步整个操作序列。同步单个操作既不安全,也更慢。
  3. 已过时的向量:在Java中,向量被认为已过时且非官方弃用。此外,向量在几乎从未完成的每个单独操作上同步。大多数Java程序员更喜欢使用ArrayList,因为如果他们需要同步,他们可能会显式地同步arrayList。

必读: Java中的 Vector 与 ArrayList