This is the 9th day of my participation in the August More Text Challenge. For details, see:August is more challenging

ArrayList is a common collection class. The underlying implementation is based on arrays, but it is not thread safe in concurrent environments. Here are some simple analyses.

The ArrayList thread is not safe

Demo

public class ArrayListNotSafeDemo {
   static List<String> list =new ArrayList<>();
    public static void main(String[] args) {
        for (int i = 0; i <3 ; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0.8)); System.out.println(list); }).start(); }}}Copy the code

Running result:

[null, 342a081b]

[null, 342a081b]

[null, 342a081b]

Change I to 10 and increase the number of concurrent requests

Exception in thread “Thread-8” java.util.ConcurrentModificationException

This is a concurrent modification exception

Source code analysis

//Appends the specified element to the end of this list.
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
Copy the code

As you can see, the add method does not have a lock, so there are thread safety issues when multiple threads operate concurrently

The solution

  • 1.Vector
  • 2.Collections.synchronizedList()
  • 3.CopyOnWrite

Vector

class VectorDemo{
    static Vector<String> vector=new Vector<>();
    public static void main(String[] args) {
        for (int i = 0; i <10 ; i++) {
            new Thread(()->{
                vector.add(UUID.randomUUID().toString().substring(0.8)); System.out.println(vector); }).start(); }}}Copy the code

After running, we found that there was no thread unsafe problem (no concurrent modification exception). When we looked at the source code of Vector, we found that its methods were all modified with synchronized, so Vector was thread safe, but its performance was low.

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
Copy the code

Collections.synchronizedList()

Collections.synchronizedList(list);
Copy the code

Similarly, the Collections utility class provides a method to lock Collections that were not previously locked

Collections.synchronizedmap (), the Collections. SynchronizedSet () and so on

Copy on write (key)

class CopyOnWriteDemo {
    static List<String> list = new CopyOnWriteArrayList<>();

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0.8)); System.out.println(list); }).start(); }}}Copy the code

To see CopyOnWriteArrayList in the add method source code

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(); }}Copy the code
  • When you add an Objcet Object to the Object[] array, you copy a new array with the length +1 of the original array, and then write the data to the new array.
  • After the write, the original reference is referred to the new array setArray(newElements), and finally the lock is released to allow other threads to write.

  • The advantage of this is that you do not need to lock the read operation, which ensures concurrency, and also reflects the idea of separating the read from the read.