实践中Java并发的ImprovedList的线程安全

时间:2016-08-26 17:14:53

标签: java concurrency

我在实践中阅读了Java Concurrency。它说这个类是线程安全的:

package net.jcip.examples;

import java.util.*;

import net.jcip.annotations.*;

/**
 * ImprovedList
 *
 * Implementing put-if-absent using composition
 *
 * @author Brian Goetz and Tim Peierls
 */
@ThreadSafe
public class ImprovedList<T> implements List<T> {
    private final List<T> list;

    public ImprovedList(List<T> list) { this.list = list; }

    public synchronized boolean putIfAbsent(T x) {
        boolean contains = list.contains(x);
        if (contains)
            list.add(x);
        return !contains;
    }

    // Plain vanilla delegation for List methods.
    // Mutative methods must be synchronized to ensure atomicity of putIfAbsent.

    public int size() {
        return list.size();
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    public boolean contains(Object o) {
        return list.contains(o);
    }

    public Iterator<T> iterator() {
        return list.iterator();
    }

    public Object[] toArray() {
        return list.toArray();
    }

    public <T> T[] toArray(T[] a) {
        return list.toArray(a);
    }

    public synchronized boolean add(T e) {
        return list.add(e);
    }

    public synchronized boolean remove(Object o) {
        return list.remove(o);
    }

    public boolean containsAll(Collection<?> c) {
        return list.containsAll(c);
    }

    public synchronized boolean addAll(Collection<? extends T> c) {
        return list.addAll(c);
    }

    public synchronized boolean addAll(int index, Collection<? extends T> c) {
        return list.addAll(index, c);
    }

    public synchronized boolean removeAll(Collection<?> c) {
        return list.removeAll(c);
    }

    public synchronized boolean retainAll(Collection<?> c) {
        return list.retainAll(c);
    }

    public boolean equals(Object o) {
        return list.equals(o);
    }

    public int hashCode() {
        return list.hashCode();
    }

    public T get(int index) {
        return list.get(index);
    }

    public T set(int index, T element) {
        return list.set(index, element);
    }

    public void add(int index, T element) {
        list.add(index, element);
    }

    public T remove(int index) {
        return list.remove(index);
    }

    public int indexOf(Object o) {
        return list.indexOf(o);
    }

    public int lastIndexOf(Object o) {
        return list.lastIndexOf(o);
    }

    public ListIterator<T> listIterator() {
        return list.listIterator();
    }

    public ListIterator<T> listIterator(int index) {
        return list.listIterator(index);
    }

    public List<T> subList(int fromIndex, int toIndex) {
        return list.subList(fromIndex, toIndex);
    }

    public synchronized void clear() { list.clear(); }
}

我不这么认为。构造函数参数list可以在另一个线程中引用。因此,如果此list不是线程安全的,则并发性可能会不一致。如果此list是线程安全的,则另一个线程使用list的锁操作它,并且使用ImprovedList的线程使用ImprovedList对象锁的锁来操作它,因此它们仍然可以使其变异同时进行。

谁能告诉我哪里错了?

3 个答案:

答案 0 :(得分:3)

书中的引文:

  只要我们的班级,这保证提供线程安全   拥有对基础List唯一的杰出参考。

所以,如果有人持有对同一个列表的引用,那么你就不能称这个类是线程安全的。但是你没有仔细阅读这本书是错的。

答案 1 :(得分:2)

你是对的。如果你想让这样的列表真的“线程安全”,那么“传入”列表只是直接使用(而不是例如复制)这一事实允许更改任何其他对象的上下文,该对象包含对该列表的引用最初的“传入”列表。

除此之外;此实现甚至允许对同步的列表进行“硬修改”;像

public T remove(int index) {

所以 - 我同意你的评估;这看起来并不完全是线程安全的。在某种程度上它是;但是,“保持90%的线程安全”,这与“完成90%”的项目一样好,对吧?!

答案 2 :(得分:1)

你错过了一个重点:

  

ImprovedList假设一旦将列表传递给其构造函数,客户端将不会再次直接使用基础列表,只能通过ImprovedList访问它。

所以你的加注在这里不适用的用例。 ImprovedListCollections.synchronizedList(List)类似,它只是一个包装类,它使用内部锁来阻止对底层列表的任何并发修改,如果提供给构造函数的列表不是直接访问而是通过{{ 1}},它是线程安全的,在这种情况下你应该理解它。

但一般来说,如果你共享提供给构造函数的(非线程安全的)ImprovedList并且你没有首先制作它的安全副本(知道创建非线程的安全副本) -safe List不是线程安全的操作),它不再是线程安全的。但请注意,共享非线程安全List是一个错误,因此它不是真正的用例。