ArrayList(Collection <! - ?extends E - > c)线程安全吗?

时间:2017-02-14 21:18:54

标签: java multithreading arraylist thread-safety

是否可以通过构造函数ArrayList创建ArrayList(Collection<? extends E> sourceCollection)的新实例而不进行任何其他同步,假设sourceCollection已同步?更具体地说,我们可以在新列表中依赖于这种情况,以准确包含调用new ArrayList(sourceCollection)时集合中的元素吗?我们可以依赖新列表处于一致状态吗?

我问这个问题是因为我在书中看到了关于如何将对象限制在线程堆栈上的局部变量的并发性的书中的例子。在这些示例中,方法传递对共享对象的引用,并且在方法内部,对象的副本存储在本地变量中 - 所有这些都没有任何同步。据称可以通过这种方式实现螺纹安全。一般的例子是:

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

2 个答案:

答案 0 :(得分:4)

提示:正如@John Bollinger所提到的,特定的ArrayList实现不在语言规范中。所以下面的内容适用于Oracle java 8实现。

是的,如果source是同步集合则是安全的,因为在这种情况下ArrayList构造函数使用源集合的toArray()方法,该方法也被同步并生成新的数据副本:

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

答案 1 :(得分:0)

您的具体问题的答案是问题的答案 - 源集合线程安全吗?

尝试了解我们如何到达目的地的最佳方式,我们可以去源头。

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

深入了解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();

返回ArrayList.toArray(假设源集合运行时类型是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);
}

最后到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是一种本机方法。在其调用堆栈顶部对c.toArray的调用使用System.arraycopy,这是一种本机方法,没有记录为线程安全。

向下移回堆栈,您的具体问题的答案就是问题的答案 - 源集合线程是否安全?

如果您使用ArrayList作为源集合,则需要确保代码中的线程安全。