java在同一个锁上同步
我正在读的一本书的摘录-
//在错误的锁上同步,因此@NotThreadSafe
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x){
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
//在相同/正确的锁上同步,因此@ThreadSafe
//如果不存在,则通过客户端锁定实现Put
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public boolean putIfAbsent(E x){
synchronized(list){
boolean absent = !list.contains(x);
if(absent)
list.add(x);
return absent;
}
}
}
接着,作者迅速转到第三个案例-
//如果没有,使用组合实现put
public class ImprovedList<E> implements List<E> {
private final List<E> list;
public ImprovedList(List<E> list){
this.list = list;
}
public synchronized boolean putIfAbsent(E x){
boolean contains = list.contains(x);
if(contains)
list.add(x);
return !contains;
}
}
上面的类@ThreadSafe
是怎样的,即使public final List<E> list;
中的list
可能not be @ThreadSafe
# 1 楼答案
我在一本书中也看到了这一点,我的代码如下
}
}
}
# 2 楼答案
如果你完全用其他线程安全的对象来创建一个对象O,那并不一定意味着O是线程安全的
如果取出
synchronized
语句,然后让两个线程同时使用相同的x
调用listHelper.putIfAbsent(x)
方法,会怎么样?两个线程都可以同时调用list.contains(x)
,并且都可以将absent
设置为true。一旦到达该点,它们都将调用list.add(x)
,线程安全列表将包含对同一对象的两个引用这似乎不是你想要的
list
的线程安全意味着,当两个线程同时调用list.add(x)
时,它们不会对列表的内部结构造成损害。这意味着list
将履行其承诺,即使多个线程同时使用它list.contains(x)
方法实现了它的承诺:它两次返回false
,因为x
不在列表中。这个list.add(x)
实现了它的承诺,它两次都在列表中加入了一个x
引用,就像它被告知的那样问题(如果取消同步)不在
list
对象的任何地方,因为列表只包含一个x
引用的要求是您的要求如果您不希望列表包含多个
x
引用,那么您必须编写代码来防止它发生。这就是synchronized
语句的作用。它通过测试x
是否在列表中,然后对它做一些事情来进行原子操作。“原子”基本上意味着,两个线程不能同时执行“线程安全”一词没有确切的含义。我没有说
ListHelper
是不线程安全的;我说把它叫做“线程安全”是不对的我只是采取了比布洛赫先生更保守的立场,仅此而已
如果你按照作者希望你使用它的方式使用它,那么这个类是线程安全的。但是通过使
list
成员public
,他也使您以非线程安全的方式使用他的类变得非常容易我以写软件为生,在我的工作中,它既不是必要的,也不足以让程序工作。我的工作是制作让顾客满意的节目。如果有一种错误的方式来使用我的程序,如果我让错误的方式比正确的方式更吸引人,那么客户不会高兴,我的老板也不会高兴,而借口“是的,但它有效”“也不会让我摆脱困境