Hello, I am watching the mountains.
Book connected to Wen, last chatted use in multiple threads ArrayList what will happen , this time we usually talk about common list: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList .
Vector
Vector
It is provided in JDK 1.0, although it is not marked Deprecated
, in fact no one uses it anymore. The main reason is poor performance and does not meet demand.
As you can see from the source code (the source code is not posted here), it Vector
is based on an array implementation. Almost all operation methods use synchronized
keywords to achieve method synchronization. This synchronization method can lock a single operation, such as multiple threads at the same time Execution add
will block execution synchronously, but multi-threaded execution add
and remove
time will not block.
However, in most scenarios where you need to lock the queue, you want to lock the entire queue, not just a single operation. In other words, Vector
it is different from our expectation, but it also increases the performance overhead caused by the synchronization operation. Therefore, it is not necessary to use the scene, you can use it ArrayList
instead, even if it is a multi-threaded situation that requires a synchronized queue, you can also use CopyOnWriteArrayList
and SynchronizedList
replace it.
ArrayList
ArrayList
It is provided in JDK 1.1. As Vector
the successor ( ArrayList
implementation is Vector
almost identical), all ArrayList
methods synchronized
are removed, synchronization is not achieved at all, and it is not thread-safe.
Its non-thread safety is also reflected in the rapid failure of iterators. After using the method iterator
and listIterator
creating the iterator, if the original ArrayList
queue is modified (add or remove), ConcurrentModificationException
an exception will be reported when the iterator is iterating . It can be seen from the source code that during the iteration process of the iterator, it will check whether the modCount
number of modifications in the queue expectedModCount
is equal to the snapshot of the number of modifications that fell when the iterator was created . Equality means no modification. The code is as follows:
private class Itr implements Iterator<E> {
// 这段代码是从 ArrayList 中摘取的
// 只留下检查方法,略过其他代码,有兴趣的可以从源码中查看
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
The third point is that in a multi-threaded scenario, adding elements may lose data, or an array out-of-bounds exception may occur. What happens when ArrayList is used in multi-threading is described in detail, so I won’t go into details here.
SynchronizedList
SynchronizedList
Yes, Collections
a static inner class, Collections.synchronizedList()
created using a static method, is a List
encapsulated implementation realized by a composite class. Most of its methods use synchronized (mutex){...}
code block synchronization, because the locked object mutex
is the same object defined in the queue object, so mutex
when the lock is locked, Vector
the entire queue can be locked, which solves the problem of not being able to lock the entire queue. . So if there are multiple threads operating add
and remove
methods at the same time , it will block synchronous execution.
ArrayList
The rapid failure of the iterator in the existing situation still exists, as the note in the source code below: If you want to use the iterator, you need to manually implement synchronization.
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
// 代码摘自 Collections,省略很多代码
public void add(int index, E element) {
synchronized (mutex) {
list.add(index, element);}
}
public ListIterator<E> listIterator() {
return list.listIterator(); // Must be manually synched by user
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index); // Must be manually synched by user
}
}
You need to pay attention to manual synchronization. Since we are concerned about global synchronization, when setting synchronization in the iterator, ensure that the locked object add
is the same as the object in the method. This will be explained in the follow-up, so it will not be expanded here.
CopyOnWriteArrayList
CopyOnWriteArrayList
It is provided since JDK 1.5, first look at add
the source code of the method:
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
// 代码摘自 CopyOnWriteArrayList,省略很多代码
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
public boolean addAll(Collection<? extends E> c) {
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (len == 0 && cs.getClass() == Object[].class)
setArray(cs);
else {
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
System.arraycopy(cs, 0, newElements, len, cs.length);
setArray(newElements);
}
return true;
} finally {
lock.unlock();
}
}
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}
}
It can be seen that CopyOnWriteArrayList
with the help ReentrantLock
of synchronization, the performance is higher synchronized
before optimization . It is also implemented through an array, but a keyword is added in front of the array , which realizes the visibility of the array in the case of multi-threading, and is safer. The more important point is that when adding elements, the implementation is to rebuild the array object and replace the original array reference. Compared with the expansion method, the space is reduced, but the performance overhead of the assignment array is also increased. When the element is acquired, there is no lock, and the data is returned directly.ReentrantLock
synchronized
CopyOnWriteArrayList
volatile
CopyOnWriteArrayList
add
ArrayList
get
CopyOnWriteArrayList
The iterator of is COWIterator
implemented, iterator
when the method is called , the snapshot of the array in the current queue is assigned to the array reference in the iterator. If the original queue is modified, the array in the queue will point to other references, and the array in the iterator will not change, so in the process of multi-threaded execution, the data in the queue can also be modified by traversing the array through the iterator. While ensuring thread safety in this way, data inconsistencies may also occur, and you can only pay more attention to the use of it.
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
}
Compare CopyOnWriteArrayList and SynchronizedList
CopyOnWriteArrayList
SynchronizedList
Both and both are synchronized, and different strategies are adopted in the implementation method, and their respective focuses are different.
CopyOnWriteArrayList
Focus on the separation of reads and writes. When a data write operation ( add
or remove
) occurs, a lock will be added. Each thread will block the execution. The execution process will create a data copy and replace the object reference; if there is a read operation ( get
or iterator
) at the same time , the read operation reads Old data may become historical data snapshots or cached data. This will cause data inconsistency when reading and writing occur at the same time, but the data will eventually be consistent. This method is almost the same as the database read-write separation mode, and many features can be compared.
SynchronizedList
Focus on strong data consistency, that is, when a data write operation ( add
or remove
) occurs, a lock will be added, each thread will block the execution, and the operation will also be blocked by the same lock get
.
From CopyOnWriteArrayList
and SynchronizedList
two different matters, it can be concluded that CopyOnWriteArrayList
the implementation of high efficiency in writing less reading, more of the scene, SynchronizedList
read and write operations efficiency is very balanced, so write to read more, write once read many little scenes efficiency will be higher CopyOnWriteArrayList
. Borrow the test results from the Internet:
Summary at the end of the article
synchronized
The performance of keywords is relatively poor before JDK 8. You can see the synchronization code implemented after JDK 1.5, many of which areReentrantLock
implemented.- In multi-threaded scenarios, in addition to synchronization, data visibility also needs to be considered, which can be achieved through
volatile
keywords. ArrayList
There is no synchronization operation at all, and it is not thread-safeCopyOnWriteArrayList
AndSynchronizedList
belong to the thread-safe queueCopyOnWriteArrayList
Realize the separation of reading and writing, suitable for scenarios where write less and read moreSynchronizedList
The data is required to be strongly consistent, which is a global locking method for the queue, and the read operation will also be lockedVector
It's just that the iterator traversal performance is very poor. If you don't consider the global lock queue, the performance of pure read operations and individual write operationsSynchronizedList
is not much different.
reference
- Why is Java Vector (and Stack) class considered obsolete or deprecated?
- CopyOnWriteArrayList and Collections.synchronizedList performance comparison
- Collections.synchronizedList, CopyOnWriteArrayList, Vector introduction, source code analysis and performance comparison
Recommended reading
Hello, I am Kanshan, public account: Kanshan’s Lodge, a 10-year-old backend, open source contributors to Apache Storm, WxJava, and Cynomys. Main job: programmer, part-time job: architect. Swim in the code world, enjoy life in drama.
Personal homepage: https://www.howardliu.cn
Personal blog post: Know the queues in Java: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList
CSDN homepage: http://blog.csdn.net/liuxinghao
CSDN blog post: Know the queues in Java : Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList