Graphic set 3: CopyOnWriteArrayList,
CopyOnWriteArrayList
The first time I saw CopyOnWriteArrayList, when studying JDBC, the Driver of each database was maintained in a CopyOnWriteArrayList. To prove this, I pasted two pieces of code, the first part of which was in com. mysql. jdbc. driver, that is, we write the Class. forName ("…") Content:
public class Driver extends NonRegisteringDriver implements java.sql.Driver{ public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }}
We can see that com. mysql. jdbc. Driver calls the registerDriver method of DriverManager. This class is under java. SQL. DriverManager:
public class DriverManager{ private static final CopyOnWriteArrayList
registeredDrivers = new CopyOnWriteArrayList(); private static volatile int loginTimeout = 0; private static volatile PrintWriter logWriter = null; private static volatile PrintStream logStream = null; private static final Object logSync = new Object(); static final SQLPermission SET_LOG_PERMISSION = new SQLPermission("setLog"); ...}
We can see that all DriverInfo is in CopyOnWriteArrayList. Now that I see CopyOnWriteArrayList, I naturally have to study why JDK uses this List.
Here are two points:
1. CopyOnWriteArrayList is located in the java. util. concurrent package. As you can imagine, this class is designed for concurrency.
2. CopyOnWriteArrayList, as the name suggests, always needs to Copy when writing, that isFor CopyOnWriteArrayList, any variable operations (add, set, remove, and so on) are accompanied by the copy operation., The underlying implementation mechanism of CopyOnWriteArrayList will be explained later
Four answers focused on CopyOnWriteArrayList
| Close note |
Conclusion |
| Whether CopyOnWriteArrayList can be empty |
Allow |
| Whether CopyOnWriteArrayList allows repeated data |
Allow |
| Whether the CopyOnWriteArrayList is ordered |
Ordered |
| Whether CopyOnWriteArrayList is thread-safe |
Thread Security |
How to add elements to CopyOnWriteArrayList
For CopyOnWriteArrayList, the principles of adding, deleting, modifying, and inserting are the same. Therefore, we can use adding elements to analyze the underlying implementation mechanism of CopyOnWriteArrayList. First look at a piece of code:
1 public static void main(String[] args)2 {3 List
list = new CopyOnWriteArrayList
();4 list.add(1);5 list.add(2);6 }
Let's take a look at what this code has done. First, a new CopyOnWriteArrayList will be instantiated in line 3rd:
public class CopyOnWriteArrayList
implements List
, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; /** The lock protecting all mutators */ transient final ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private volatile transient Object[] array; ...}
public CopyOnWriteArrayList() { setArray(new Object[0]);}
final void setArray(Object[] a) { array = a;}
As you can see, for CopyOnWriteArrayList, the bottom layer is an Object [] array, and a CopyOnWriteArrayList is instantiated. It is very simple to use graphs:
In this way, the Object array points to an array with the size of 0. Next, let's take a look at what is done by adding an integer 1 in row 4th. The source code of add is:
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();}}
Draw a picture to show:
Each step is clearly displayed on the figure, and one add operation goes through several steps:
1. Lock
2. Get the original array, get the size of the new array (the size of the original array + 1), and create a new array.
3. Copy the elements of the original array to the new array.
4. Set the last position of the new array to the element to be added (because the size of the new array is based on the size of the original array + 1)
5. Point the Object array reference to the new array.
6. Unlock
The whole process looks like the expansion of ArrayList. With this foundation, let's take a look at what an integer 2 has done in the 5th rows. This should be very simple, or draw a picture to represent it:
Similar to the previous one, I will not explain it.
In addition, the insert, delete, and modify operations are the same. Each operation is based on a copy of the Object [] array. If the above process is understood, it is not difficult to study the source code of insertion, deletion, and modification.
Common List Defects
Commonly used lists include ArrayList, rule List, and Vector. The first two are non-thread-safe, and the last one is thread-safe. I have a scenario where two threads operate on the same List and iterate and delete the same List, just like the following code:
public static class T1 extends Thread{ private List
list; public T1(List
list) { this.list = list; } public void run() { for (Integer i : list) { } }}public static class T2 extends Thread{ private List
list; public T2(List
list) { this.list = list; } public void run() { for (int i = 0; i < list.size(); i++) { list.remove(i); } }}
First, I put ArrayList in these two threads and start these two threads:
public static void main(String[] args){ List
list = new ArrayList
(); for (int i = 0; i < 10000; i++) { list.add(i); } T1 t1 = new T1(list); T2 t2 = new T2(list); t1.start(); t2.start();}
The running result is:
Exception in thread "Thread-0" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at com.xrq.test60.TestMain$T1.run(TestMain.java:19)
Replace ArrayList with 'arraylist', and the code of the main function will not be pasted. The running result is:
Exception in thread "Thread-0" java.util.ConcurrentModificationException at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:761) at java.util.LinkedList$ListItr.next(LinkedList.java:696) at com.xrq.test60.TestMain$T1.run(TestMain.java:19)
Some people may think that these two threads are non-thread-safe classes, so they do not work. In fact, this problem has nothing to do with thread security. replace it with a Vector to check the running result:
Exception in thread "Thread-0" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at com.xrq.test60.TestMain$T1.run(TestMain.java:19)
Although Vector is thread-safe, it is only relative thread-safe, not absolute thread-safe. It can only ensure that a single operation of addition, deletion, modification, and query must be atomic, it will not be interrupted, but it cannot guarantee thread security if combined. For example, if thread 1 traverses elements in a Vector and thread 2 deletes elements in a Vector, concurrent modification exceptions are inevitable, that isFail-fast.
Functions of CopyOnWriteArrayList
Modify the code above and use CopyOnWriteArrayList:
public static void main(String[] args){ List
list = new CopyOnWriteArrayList
(); for (int i = 0; i < 10; i++) { list.add(i); } T1 t1 = new T1(list); T2 t2 = new T2(list); t1.start(); t2.start();}
You can run this code without any problems.
We can see that the number of elements is reduced a little, because we can see from the above analysis that the disadvantage of CopyOnWriteArrayList is that the modification is very expensive, and each modification is accompanied by an array copy; but at the same time, the advantage is also very obvious, that is, there will be no thread security issues in concurrency, that is, absolute thread security, which is why we need to use CopyOnWriteArrayList.
In addition, there are two points that must be discussed. I think the CopyOnWriteArrayList concurrency component actually reflects two very important distributed concepts:
(1) read/write splitting
When we read the CopyOnWriteArrayList, we read the Object [] array in the CopyOnWriteArrayList. However, when we modify the Object, we operate on a new Object [] array, read and Write operations are not the same object. This is read/write splitting. This technology is widely used in databases. In high concurrency, in order to relieve the pressure on the database, read/write splitting should be performed on the database even if the database is cached, And the read database should be used for reading, when writing, use the write database, and then synchronize between the Read and Write databases to avoid too many IO operations to read and write in the same database.
(2) final consistency
For CopyOnWriteArrayList, thread 1 does not necessarily read the latest data from the set. Because thread 2, thread 3, and thread 4 modify the data in the CopyOnWriteArrayList, but thread 1 obtains the oldest Object [] array, the newly added data does not exist, therefore, the content read by thread 1 may not be accurate. However, although the data is inconsistent with thread 1, it must be consistent with the subsequent threads, the Object [] array they get must be the Object array [] After all three threads have completed the operation. This is the final consistency. Eventual consistency is also very important for distributed systems. It increases availability and partition fault tolerance of the entire distributed system by tolerating data inconsistencies for a certain period of time. Of course, final consistency is not applicable to any scenario. For example, if the real-time data requirements of system users such as railway station ticket sales are very high, they must be highly consistent.
Finally, with the increase of elements in the CopyOnWriteArrayList, the modification cost of the CopyOnWriteArrayList will become more and more expensive. Therefore,CopyOnWriteArrayList is applicable to concurrent read operations that are far greater than modification operations..