客户端锁定是否违反同步策略的封装?
正如Java_author提到的,客户端锁定是否违反同步策略的封装?
客户端锁定需要守着使用一些对象X与锁定的客户端代码,X使用来保护它自己的状态。
在下面的代码即对象X是list
。以上说的是,使用锁所拥有的ListHelper
类型的对象来同步putIfAbsent()
,是一个错误的锁。
package compositeobjects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ListHelper<E> {
private 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;
}
}
}
但是,Java的作者说,
客户端锁定有很多共同的类扩展,他们俩夫妇的派生类的基础实施的行为类。正如扩展违反实现封装[EJ条款14]一样,客户端锁定违反了对同步策略的封装。
我的理解是,Collections.synchronizedList()
返回的嵌套类实例,也使用了list
所拥有的锁对象。
为什么ListHelper
客户端锁定(与list
)的使用,违反了同步策略的封装?
您正在依靠synchronizedList
将自己用作显示器这一事实,目前情况恰好如此。
你甚至要依赖于该synchronizedList
使用实现同步,这也恰好是目前真正的事实(这是一个合理的假设,但它不是一个是必要的)。
有些方法可能会改变synchronizedList
的实现,导致您的代码无法正常工作。
例如,the constructor of synchronizedList
:
SynchronizedList(List<E> list) {
super(list);
// ...
}
可改为
SynchronizedList(List<E> list) {
super(list, new Object());
// ...
}
现在,在SynchronizedList
执行方法使用的mutex
场不再this
(有效) ,因此list
上的外部同步将不再起作用。
随着中说,在使用synchronized (list)
确实有预期的效果在Javadoc描述,所以这种行为不会改变的事实,所以你现在正在做的是精绝;它的设计使用了漏洞抽象,所以如果你从头开始做类似的事情,就不应该这样设计,但是漏洞抽象的属性被记录下来。
为什么'如果实施改变'代码无法正常工作? imho'synchronized(list)'是必须的,否则'Set'-wise操作将不起作用。 –
@ M.leRutte想想如果[这行代码]会发生什么(http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ Collections.java#2399)改为'super(list,new Object())'(或['this line'](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/) openjdk/8u40-b25/java/util/Collections.java#2006)改为'this.mutex = new Object()')。互斥体不再是列表本身,所以在'list'上同步将不起作用。 –
啊,是的,如果列表将使用不同于'this'的监视器,并且另一个线程将访问列表,并且示例代码将被执行,那么它将出错。我懂了。无论如何,看到我的答案,这段代码已经很容易出错,因为它需要所有代码才能通过实用程序类。 –
你的代码基本上创建了一个同步集。它只添加了元素,如果它不在列表中,那么就是set的定义。
无论同步列表如何执行自己的锁定,您必须提供自己的锁定机制,因为有两个对同步列表的调用,其中列表将释放其锁定。因此,如果两个线程添加相同的对象,他们都可以通过contains
检查并将其添加到列表中。化合物synchronize
确保它不是。最重要的是,所有的列表使用代码都会经过您的实用程序类,否则它将会失败。
正如我在评论中所写的,使用synchronized set
可以实现完全相同的行为,这也将确保在锁定整个操作时尚未添加该元素。通过使用这个同步的设置访问和修改而不使用你的工具类是可以的。
编辑:
如果你的代码需要一个列表,而不是一组,和LinkedHashSet
是不是我会创建一个新的同步列表我自己的选择:你是靠
public class SynchronizedList<E> implements List<E> {
private List<E> wrapped = new ArrayList<E>();
....
@override
public int size() {
synchronized(this) {
return wrapped.size();
}
}
....
@override
public void add(E element) {
synchronized(this) {
boolean absent = !wrapped.contains(x);
if(absent) {
wrapped.add(element);
}
return absent;
}
}
啊,我需要基于位置的元素集合,但也需要'putIfAbsent'。这是一个正确的用法吗? – overexchange
是否会包装['LinkedHashSet'](https://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html)?保持顺序,但它没有'get(index)',这是我个人很少需要的。 –
上面的使用案例是关于*为现有的线程安全类*添加功能,因此,* putIfAbsent *是今天为现有的“ListHelper
因为'synchronizedList'使用自己作为监视器,而目前情况恰好如此。如果此实现更改,您的代码将无法正常工作。 –
@AndyTurner明白了。通过'ArrayList'监控模式是个好主意。你建议吗? – overexchange
您知道您的实用程序类可以被['synchronizedSet'](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedSet(java.util。组))? –