CopyOnWriteList Secret

List of concurrent containers -CopyOnWriteList

Vector和SynchronizedList

ArrayList is used instead of Vector, Vector is thread-safe container, because it is in the method are added synchronized synchronized keyword

E.g:

public synchronized void copyInto(Object[] anArray) {
        System.arraycopy(elementData, 0, anArray, 0, elementCount);
}

/**
 * Trims the capacity of this vector to be the vector's current
 * size. If the capacity of this vector is larger than its current
 * size, then the capacity is changed to equal the size by replacing
 * its internal data array, kept in the field {@code elementData},
 * with a smaller one. An application can use this operation to
 * minimize the storage of a vector.
 */
public synchronized void trimToSize() {
    modCount++;
    int oldCapacity = elementData.length;
    if (elementCount < oldCapacity) {
        elementData = Arrays.copyOf(elementData, elementCount);
    }
}

The method is also within the Collections.synchronizedList method adds the synchronized keyword

problem

public static void main(String[] args) {
    Vector vector = new Vector();
    vector.add("a");
    vector.add("b");
    vector.add("c");
    new Thread(()->{
        getLast(vector);
    }).start();
    new Thread(()->{
        removeLast(vector);
    }).start();
    
    new Thread(()->{
        getLast(vector);
    }).start();
    new Thread(()->{
        removeLast(vector);
    }).start();
}

private static void removeLast(Vector vector) {
    int index  = vector.size() - 1;
    vector.remove(index);
}

private static Object getLast(Vector vector) {
    int index = vector.size() - 1;
    return vector.get(index);
}

Above such code may be abnormal happens when alternate thread execution, and our own method getLast removeLast no guarantee atomicity

To solve the above problem is very simple, that is the way we wrote it myself to do the synchronization process, such as adding synchronized keyword, like this example below:

private synchronized static void removeLast(Vector vector) {
    int index  = vector.size() - 1;
    vector.remove(index);
}

private synchronized static Object getLast(Vector vector) {
    int index = vector.size() - 1;
    return vector.get(index);
}

Look through the Vector collection time

For example, traversing get vector.size () 3, when the other threads of the container has been modified, then the size of the container 2, traversing get get (3) will appear abnormal

If for-each (iterator) to do the above operations, throw exception ConcurrentModificationException

To solve this problem, but also in the traversal methods for vector lock

CopyOnWriteList

In general, we think: CopyOnWriteArrayList List is synchronized alternatives, CopyOnWriteArraySet alternatives are synchronized Set

Whether Hashtable to ConcurrentHashMap, Vector to CopyOnWriteArrayList.
Under JUC container supports concurrent thread-safe class compared to the older generation, we are doing the optimized lock granularity

achieve

What is the COW

If there are multiple callers (callers) at the same time requesting the same resources (such as data storage on memory or disk), they will get the same common pointers point to the same resource,
until a caller tries to modify the content of the resource, the system only a copy of a copy will really dedicated (private copy) to the caller,
while other callers have seen the original resource remains unchanged.
The advantage is you can share the same resources if a caller does not modify the resources, there will be a copy of the (private copy) is established, multiple callers just read operation.

Look at the data structure of CopyOnWriteArrayList

/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();

/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

/**
 * Gets the array.  Non-private so as to also be accessible
 * from CopyOnWriteArraySet class.
 */
final Object[] getArray() {
    return array;
}

/**
 * Sets the array.
 */
final void setArray(Object[] a) {
    array = a;
}

ConcurrentHashMap data structure than it is very simple to use Lock to lock (when modifying data), using the Object array to keep data

CopyOnWriteArrayList features

  • CopyOnWriteArrayList container is thread safe (as opposed to ArrayList), achieved by way of copying the underlying array.
  • CopyOnWriteArrayList in traversal using ConcurrentModificationException not throw an exception, and when he traversed without additional lock
  • Elements may be null

Secret

CopyOnWriteList if done under a concurrent environment traverse the container without exception it?

Next we look iterator method returns COWIterator class. We can see how this class is composed of

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;
    }

    public boolean hasNext() {
        return cursor < snapshot.length;
    }

    public boolean hasPrevious() {
        return cursor > 0;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        if (! hasNext())
            throw new NoSuchElementException();
        return (E) snapshot[cursor++];
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        if (! hasPrevious())
            throw new NoSuchElementException();
        return (E) snapshot[--cursor];
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor-1;
    }

    /**
     * Not supported. Always throws UnsupportedOperationException.
     * @throws UnsupportedOperationException always; {@code remove}
     *         is not supported by this iterator.
     */
    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
     * Not supported. Always throws UnsupportedOperationException.
     * @throws UnsupportedOperationException always; {@code set}
     *         is not supported by this iterator.
     */
    public void set(E e) {
        throw new UnsupportedOperationException();
    }

    /**
     * Not supported. Always throws UnsupportedOperationException.
     * @throws UnsupportedOperationException always; {@code add}
     *         is not supported by this iterator.
     */
    public void add(E e) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        Object[] elements = snapshot;
        final int size = elements.length;
        for (int i = cursor; i < size; i++) {
            @SuppressWarnings("unchecked") E e = (E) elements[i];
            action.accept(e);
        }
        cursor = size;
    }
}

Can be seen that there is a class Object [] array such snapshot, according to the code can be maintained until the array is an array to be traversed, it is CopyOnWriteArrayList the corresponding
array data stored

We know from the above, save the iterator is data acquisition CopyOnWriteList collection iterator. So modify the original data set will not affect the traversal iterator in an iterative process, so CopyOnWriteList can not guarantee real-time data consistency.

Guess you like

Origin www.cnblogs.com/watertreestar/p/11780254.html