Java ArrayList同步化
ArrayList的实现在默认情况下是不同步的。这意味着如果一个线程在结构上修改它,并且多个线程同时访问它,它必须在外部进行同步。结构上的修改意味着从列表中增加或删除元素,或者明确地调整后备数组的大小。改变现有元素的值不属于结构性修改。
有两种方法可以创建一个同步ArrayList
1.Collections.synchronizedList()方法。
2.使用CopyOnWriteArrayList。
方法一:使用 Collections.synchronizedList() 方法
为了进行串行访问,对后备列表的所有访问都必须通过返回的列表来完成。当用户在返回的列表上进行迭代时,必须对其进行 手动 同步。
public static List <T> synchronizedList(List<T> list)
- 接受一个List,它可以是 List 接口的实现,例如ArrayList, LinkedList。
- 返回一个由指定列表支持的同步(线程安全)列表。
- 参数list是要被包装成同步列表的列表。
- T代表通用
// Java program to demonstrate working of
// Collections.synchronizedList
import java.util.*;
class GFG
{
public static void main (String[] args)
{
List<String> list =
Collections.synchronizedList(new ArrayList<String>());
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());
}
}
}
输出
practice
code
quiz
方法2:使用CopyOnWriteArrayList
CopyOnWriteArrayList<T> threadSafeList = new CopyOnWriteArrayList<T>();
- 创建一个空的List。
- 它实现了 List 接口。
- 它是ArrayList的一个线程安全的变体。
- T代表通用
ArrayList的线程安全变体,其中所有的变异操作(例如,添加、设置、删除……)都是通过创建一个底层 数组 的单独副本来实现的 。 它通过创建一个单独的List副本来实现线程安全,这与向量或其他用于提供线程安全的集合不同。
- 当你不能或不想 同步 遍历,但又需要防止并发线程之间的干扰时,它就很有用。
- 它的 成本很高 ,因为它涉及到每个写操作的单独的 数组拷贝 (例如,添加、设置、删除。
- 当你有 List ,需要遍历它的元素并且不经常修改它时,它是非常有效的。
即使copyOnWriteArrayList在迭代器创建后被修改,该迭代器也不会抛出 ConcurrentModificationException 。迭代器在ArrayList的单独副本上进行迭代,同时在ArrayList的另一个副本上进行写操作。
// 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<String> threadSafeList
= new CopyOnWriteArrayList<String>();
// 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<String> 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 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<String> threadSafeList =
new CopyOnWriteArrayList<String>();
// 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<String> 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的其他构造函数
- **CopyOnWriteArrayList(Collection <? extends E> c) **: 创建一个包含指定集合元素的列表,其顺序是由集合的迭代器返回。
-
CopyOnWriteArrayList(E[] toCopyIn) : 创建一个包含给定数组副本的列表。
当向量是同步的,为什么使用ArrayList ?
- 性能: Vector是 同步的 和线程安全的,正因为如此,它比ArrayList稍慢。
- 功能: Vector在每个单独的操作层面上进行同步。一般来说,程序员喜欢对整个操作序列进行同步。同步单个操作既不安全也比较慢。
- 矢量过时: 矢量被认为是过时的,在java中被非官方地废弃。而且,向量在每个单独的操作上都要进行同步,这几乎是不可能的。大多数java程序员喜欢使用ArrayList,因为如果他们需要同步,他们可能会明确地同步arrayList。
极客教程