我在实践中阅读了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
对象锁的锁来操作它,因此它们仍然可以使其变异同时进行。
谁能告诉我哪里错了?
答案 0 :(得分:3)
书中的引文:
只要我们的班级,这保证提供线程安全
拥有对基础List
唯一的杰出参考。
所以,如果有人持有对同一个列表的引用,那么你就不能称这个类是线程安全的。但是你没有仔细阅读这本书是错的。
答案 1 :(得分:2)
你是对的。如果你想让这样的列表真的“线程安全”,那么“传入”列表只是直接使用(而不是例如复制)这一事实允许更改任何其他对象的上下文,该对象包含对该列表的引用最初的“传入”列表。
除此之外;此实现甚至允许对不同步的列表进行“硬修改”;像
public T remove(int index) {
所以 - 我同意你的评估;这看起来并不完全是线程安全的。在某种程度上它是;但是,“保持90%的线程安全”,这与“完成90%”的项目一样好,对吧?!
答案 2 :(得分:1)
你错过了一个重点:
ImprovedList
假设一旦将列表传递给其构造函数,客户端将不会再次直接使用基础列表,只能通过ImprovedList
访问它。
所以你的加注在这里不适用的用例。 ImprovedList
与Collections.synchronizedList(List)
类似,它只是一个包装类,它使用内部锁来阻止对底层列表的任何并发修改,如果提供给构造函数的列表不是直接访问而是通过{{ 1}},它是线程安全的,在这种情况下你应该理解它。
但一般来说,如果你共享提供给构造函数的(非线程安全的)ImprovedList
并且你没有首先制作它的安全副本(知道创建非线程的安全副本) -safe List不是线程安全的操作),它不再是线程安全的。但请注意,共享非线程安全List
是一个错误,因此它不是真正的用例。