Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is Java's CopyOnWriteArrayList?

2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly explains "what is the CopyOnWriteArrayList of Java". Interested friends may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what is the CopyOnWriteArrayList of Java"?

01 、 Vector

Vector's source documentation says bluntly, "if thread safety is not needed, ArrayList is recommended instead of Vector." To be honest, I have rarely used Vector in my programming career for more than a decade, because its thread safety is based on the addition of the synchronized keyword to every method, and the high granularity of locks means poor performance.

Public synchronized boolean add (E e) {

ModCount++

Add (e, elementData, elementCount)

Return true

}

Public synchronized E remove (int index) {

ModCount++

If (index > = elementCount)

Throw new ArrayIndexOutOfBoundsException (index)

E oldValue = elementData (index)

Int numMoved = elementCount-index-1

If (numMoved > 0)

System.arraycopy (elementData, index+1, elementData, index

NumMoved)

ElementData [--elementCount] = null; / / Let gc do its work

Return oldValue

}

Even methods such as size () have added synchronized, and you can imagine how extravagant and wasteful Vector is.

If you don't know much about the synchronized keyword, you can click on the link below to view an article I wrote earlier.

I can't believe you don't know how to use synchronized.

In the case of high concurrency, performance is generally required, and Vector is obviously not qualified, so it is "deserved" to be left in the corner.

02 、 SynchronizedList

Some students may say that you can use Collections.synchronizedList () to make ArrayList thread-safe.

Public static List synchronizedList (List list) {

Return (list instanceof RandomAccess?

New Collections.SynchronizedRandomAccessList (list):

New Collections.SynchronizedList (list))

}

Both SynchronizedRandomAccessList and SynchronizedList do not use the synchronized keyword at the method level, but instead use the synchronized (this) block in the method body.

Public void add (int index, E element) {

Synchronized (mutex) {list.add (index, element);}

}

Public E remove (int index) {

Synchronized (mutex) {return list.remove (index);}

}

Where mutex is the this keyword, which is the current object.

Final Object mutex; / / Object on which to synchronize

SynchronizedCollection (Collection c) {

This.c = Objects.requireNonNull (c)

Mutex = this

} 03 、 ConcurrentModificationException

ConcurrentModificationException, I wonder if the students have encountered it. Let me first knock on a piece of code to make it happen once and let the students know it.

List list = new ArrayList ()

List.add (Silent King II)

List.add (Silent King III)

List.add ("A really interesting programmer with an article")

For (String str: list) {

If (Silent King II) .equals (str)) {

List.remove (str)

}

}

System.out.println (list)

Running this code will throw a ConcurrentModificationException:

Exception in thread "main" java.util.ConcurrentModificationException

At java.base/java.util.ArrayList$Itr.checkForComodification (ArrayList.java:1012)

At java.base/java.util.ArrayList$Itr.next (ArrayList.java:966)

It can be found through the stack information of the exception that the exception occurred in the checkForComodification () method of the inner class Itr of ArrayList.

Final void checkForComodification () {

If (modCount! = expectedModCount)

Throw new ConcurrentModificationException ()

}

That is, when the checkForComodification () method is executed, a ConcurrentModificationException exception is thrown when it is found that modCount and expectedModCount are not equal.

What causes it? The previous code didn't call the checkForComodification () method either!

Then we can only take a look at the decompiled bytecode. It turns out that the syntax sugar of for-each is implemented through Iterator.

List list = new ArrayList ()

List.add (Silent King II)

List.add (Silent King III)

List.add ("A really interesting programmer with an article")

Iterator var3 = list.iterator ()

While (var3.hasNext ()) {

String str = (String) var3.next ()

If (Silent King II) .equals (str)) {

List.remove (str)

}

}

System.out.println (list)

When list.iterator () is executed, what is actually returned is the inner class Itr of ArrayList.

Public Iterator iterator () {

Return new ArrayList.Itr ()

}

The iterator Iterator is fail-fast, if in any way including remove and

Add) if you make changes to the iterator, ConcurrentModificationException will be thrown.

When the iterator executes the remove () method, it adds 1 to the modCount. The fastRemove () method is called inside the remove () method.

Private void fastRemove (Object [] es, int I) {

ModCount++

Final int newSize

If ((newSize = size-1) > I)

System.arraycopy (es, I + 1, es, I, newSize-I)

Es [size = newSize] = null

}

When the next time next () executes the checkForComodification () method, it turns out that modCount is 4 and expectedModCount is 3, so an exception is thrown.

The reason why ConcurrentModificationException is thrown in the case of single thread is to avoid the possibility of List modification by other threads in advance without taking any risk in the case of multi-thread concurrency.

The iterator returned by ArrayList is fail-fast, so is Vector's, and so is SynchronizedList's. This means that there will be problems when they add and delete operations through for-each traversal in a multithreaded environment.

04 、 CopyOnWriteArrayList

Look how much effort I put into bringing out CopyOnWriteArrayList.

List list = new CopyOnWriteArrayList ()

List.add (Silent King II)

List.add (Silent King III)

List.add ("A really interesting programmer with an article")

For (String str: list) {

If (Silent King II) .equals (str)) {

List.remove (str)

}

}

System.out.println (list)

Replace ArrayList with CopyOnWriteArrayList, and the program can be executed normally, and the output is shown below.

[silent Wang San, a really interesting programmer with an article]

The reason why the ConcurrentModificationException exception is not thrown is that the CopyOnWriteArrayList is fail-safe, the iterator traverses the original array, and when remove, remove is the copied new array, and then assign the new array to the original array.

However, any changes to CopyOnWriteArrayList after getting the iterator will not be reflected in the iterator in a timely manner.

CopyOnWriteArrayList list1 =

New CopyOnWriteArrayList (new String [] {"Silent King II", "Silent King 3"})

Iterator itr = list1.iterator ()

List1.add (Silent King IV)

While (itr.hasNext ()) {

System.out.print (itr.next () + "")

}

Silent King IV will not appear in the output.

Silent King two Silent King three

ArrayList's iterator Itr supports remove.

List list = new ArrayList ()

List.add (Silent King II)

List.add (Silent King III)

List.add ("A really interesting programmer with an article")

Iterator var3 = list.iterator ()

While (var3.hasNext ()) {

String str = (String) var3.next ()

If (Silent King II) .equals (str)) {

Var3.remove ()

}

}

System.out.println (list)

The result of the program output is as follows:

[silent Wang San, a really interesting programmer with an article]

CopyOnWriteArrayList's iterator COWIterator does not support remove.

Public void remove () {

Throw new UnsupportedOperationException ()

}

CopyOnWriteArrayList implements the List interface, however, it is not under the java.util package, but under the java.util.concurrent package, it is counted as an enhanced version of ArrayList, thread-safe.

As the name implies, CopyOnWriteArrayList will be copied first when doing write operations (add, set, remove), and the underlying layer is achieved through array replication.

In Java 8, CopyOnWriteArrayList's addition, deletion and modification operation method uses ReentrantLock (reentrant locks, a thread can still lock repeatedly after it has acquired the lock, without blocking itself).

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 ()

}

}

When Java 14, it has been changed to synchronized block.

Public boolean add (E e) {

Synchronized (lock) {

Object [] es = getArray ()

Int len = es.length

Es = Arrays.copyOf (es, len + 1)

Es [len] = e

SetArray (es)

Return true

}

}

Where lock is an Object object (annotated that it has something to do with ReentrantLock).

/ * *

* The lock protecting all mutators. (We have a mild preference

* for builtin monitors over ReentrantLock when either will do.)

, /

Final transient Object lock = new Object ()

Students can try it out whether it is better to use ReentrantLock or synchronized block. However, in other details, Java 14 is written more succinctly than Java 8, with one less newElements variable being created.

Let's take a look at the set () method:

Public E set (int index, E element) {

Synchronized (lock) {

Object [] es = getArray ()

E oldValue = elementAt (es, index)

If (oldValue! = element) {

Es = es.clone ()

Es [index] = element

}

/ / Ensure volatile write semantics even when oldvalue = = element

SetArray (es)

Return oldValue

}

}

The synchronized block is also used and the encapsulated clone () method is called to copy it.

Then take a look at the remove () method:

Public E remove (int index) {

Synchronized (lock) {

Object [] es = getArray ()

Int len = es.length

E oldValue = elementAt (es, index)

Int numMoved = len-index-1

Object [] newElements

If (numMoved = = 0)

NewElements = Arrays.copyOf (es, len-1)

Else {

NewElements = new Object [len-1]

System.arraycopy (es, 0, newElements, 0, index)

System.arraycopy (es, index + 1, newElements, index

NumMoved)

}

SetArray (newElements)

Return oldValue

}

}

Synchronized blocks are required, as is array copy (System.arraycopy ()).

Unlike Vector, the get () and size () methods of CopyOnWriteArrayList are no longer locked.

Public int size () {

Return getArray () length

}

Public E get (int index) {

Return elementAt (getArray (), index)

} at this point, I believe you have a deeper understanding of "what is the CopyOnWriteArrayList of Java". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report