简体   繁体   中英

ArrayList(Collection<? extends E> c) thread safe?

Is it thread safe to create a new instance of ArrayList via constructor ArrayList(Collection<? extends E> sourceCollection) without any additional sychronization, supposing that sourceCollection is synchronized? More specifically, can we rely in that case on the new list to contain exactly the elements that were in the collection at the time new ArrayList(sourceCollection) was invoked? And can we rely on the new list to be in a consistent state?

I'm asking this question because I've seen examples in books on concurrency of how to confine objects to local variables on a thread's stack. In these examples a method is passed a reference to a shared object, and inside the method a copy of the object is stored in a local variable -- all this without any synchronization. It is claimed that thread safety can be achieved in this way. A general example would be:

public void someMethod(Collection<String> source) {
    List<String> localList = new ArrayList<>(source);
    ...
}

Hint : as @John Bollinger justly mentioned, particular ArrayList implementation is not covered by language specification. So written below is true for Oracle java 8 implementation.


Yes, it is safe if source is synchronised collection, because ArrayList constructor in this case uses toArray() method of source collection, which is synchronised as well and produce new copy of data:

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    // ...
}

The answer to your specific question is the answer to the question - is the source collection thread safe?

The best way to go about trying to understand how we get there, we can go to the source.

Starting with ArrayList

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

Diving into the documentation for Collection.toArray

/**
 * Returns an array containing all of the elements in this collection.
 * If this collection makes any guarantees as to what order its elements
 * are returned by its iterator, this method must return the elements in
 * the same order.
 *
 * <p>The returned array will be "safe" in that no references to it are
 * maintained by this collection.  (In other words, this method must
 * allocate a new array even if this collection is backed by an array).
 * The caller is thus free to modify the returned array.
 *
 * <p>This method acts as bridge between array-based and collection-based
 * APIs.
 *
 * @return an array containing all of the elements in this collection
 */
Object[] toArray();

Back to ArrayList.toArray (Assuming the source collections runtime type is an ArrayList)

/**
 * Returns an array containing all of the elements in this list
 * in proper sequence (from first to last element).
 *
 * <p>The returned array will be "safe" in that no references to it are
 * maintained by this list.  (In other words, this method must allocate
 * a new array).  The caller is thus free to modify the returned array.
 *
 * <p>This method acts as bridge between array-based and collection-based
 * APIs.
 *
 * @return an array containing all of the elements in this list in
 *         proper sequence
 */
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

and finally onto Array.copyOf

/**
 * Copies the specified array, truncating or padding with nulls (if necessary)
 * so the copy has the specified length.  For all indices that are
 * valid in both the original array and the copy, the two arrays will
 * contain identical values.  For any indices that are valid in the
 * copy but not the original, the copy will contain <tt>null</tt>.
 * Such indices will exist if and only if the specified length
 * is greater than that of the original array.
 * The resulting array is of the class <tt>newType</tt>.
 *
 * @param <U> the class of the objects in the original array
 * @param <T> the class of the objects in the returned array
 * @param original the array to be copied
 * @param newLength the length of the copy to be returned
 * @param newType the class of the copy to be returned
 * @return a copy of the original array, truncated or padded with nulls
 *     to obtain the specified length
 * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
 * @throws NullPointerException if <tt>original</tt> is null
 * @throws ArrayStoreException if an element copied from
 *     <tt>original</tt> is not of a runtime type that can be stored in
 *     an array of class <tt>newType</tt>
 * @since 1.6
 */
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

System.arraycopy is a native method. The call to c.toArray at the top of its invocation stack uses System.arraycopy which is a native method which isn't documented to be thread safe.

Moving back down the stack, the answer to your specific question is the answer to the question - is the source collection thread safe?

If you are using an ArrayList as the source collection, you will need to ensure thread safety in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM