Thread-safe list of synchronizedList and CopyOnWriteArrayList

In the last article we've covered some of the other list collection, such as ArrayList, linkedlist and so on. Unclear may have a look on the article www.jianshu.com/p/6227ab5b3... but appear to be thread safe ArrayList these problems, how do we solve it? The next step is to introduce our thread-safe list collection synchronizedList and CopyOnWriteArrayList.

一、synchronizedList

synchronizedList of use:

public void test(){
        ArrayList<String> list = new ArrayList<>();
        List<String> sycList = Collections.synchronizedList(list);
        sycList.add("a");
        sycList.remove("a");
    }
复制代码

We can see from the above mode of use, synchronizedList synchronizedList is a collection of the List as a set of parameters to create.

Why synchronizedList is thread safe? We first look at his source:

        @Override
        public int size() {
            synchronized(mutex) {
                return backingList.size();
            }
        }

        @Override
        public boolean isEmpty() {
            synchronized(mutex) {
                return backingList.isEmpty();
            }
        }

        @Override
        public Object[] toArray() {
            synchronized(mutex)  {
                return backingList.toArray();
            }
        }

        @Override
        public boolean add(T e) {
            synchronized(mutex) {
                return backingList.add(e);
            }
        }

        @Override
        public boolean remove(Object o) {
            synchronized(mutex) {
                return backingList.remove(o);
            }
        }
复制代码

We probably put up some source commonly used method, we can see from the above source code, in fact synchronizedList thread safety reasons because it is in almost every way are synchronized using a synchronization lock.

Why do I need to traverse synchronizedList lock?

SynchronizedList use of official documents is given in the following way:

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }
复制代码

In the above, we can see the source code, official documents is recommended when we traverse the lock processing. But since internal methods and added a lock, why the need to lock when traverse it? We look at its traversal methods:

        @Override
        public Iterator<T> iterator() {
            return backingList.iterator();
        }
复制代码

As can be seen from the above source, although in most internal methods have been added to the lock, but the lock did not deal with iterator method. So if we do not lock when traverse can cause the problem? Just when we traverse, without locked, at this time if there are other threads on this collection add or remove operation, so this time will result in loss of data or dirty data problem, so if we have data higher, wants to avoid problems in this area, then, at the time of traversal also need to lock for processing. But since it is locked for processing using synchronized, it certainly can not avoid some lock overhead. Is there a better way of doing things efficiently? That is our other main concurrent collection CopyOnWriteArrayList.

二、CopyOnWriteArrayList

CopyOnWriteArrayList is when a modify operation, copy a new array of related operations, after executing the operation to modify the original collection point to the new collection to complete the modification operation. Specific source as follows:

    /** The array, accessed only via getArray/setArray. */  
    private volatile transient Object[] array;//保证了线程的可见性  

    public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);  //copy一份比当前数组长度+1的array数组
            else {
                newElements = new Object[len + 1];         //将add的参数赋值
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);           //将原数组指向新的数组
        } finally {
            lock.unlock();
        }
    }

    public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = get(elements, index);
            int numMoved = len - index - 1;    
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));    //copy一份比当前数组长度-1的array数组
            else {
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);      //将原数组指向新的数组
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 将原数组指向新的数组
     */
    final void setArray(Object[] a) {
        array = a;
    }

复制代码

From the above we can see the source, which in the implementation of the method add and remove methods respectively creates an array and the current array lengths +1 -1, copy the data to the new array, and the edit operation. After modifying the method call setArray to point to the new array. Use ReentrantLock is reentrant locks to ensure that no more than one thread at the same time a new copy of the array, causing chaos throughout the process. And the use of volatile modified array to ensure the visibility of the modified. Read and write operations independently of each other, so the overall efficiency of the whole process is very high.

to sum up

synchronizedList data suitable for demanding conditions, but because the reading and writing all locked, all low efficiency. CopyOnWriteArrayList high efficiency, suitable for reading and writing little scenes, because when read in reading a collection of old, so it's not real-time high.

Guess you like

Origin juejin.im/post/5d40105ae51d4561c273a649