Java 当延迟锁定时,在保持特定监视器的同时修改对象的易失性布尔字段的处理方法是什么?
我实现了一个延迟锁定列表,它支持以下方法:boolean add(T项)、boolean remove(T项)、boolean contains(T项) 例如,添加方法:Java 当延迟锁定时,在保持特定监视器的同时修改对象的易失性布尔字段的处理方法是什么?,java,multithreading,concurrency,locking,visibility,Java,Multithreading,Concurrency,Locking,Visibility,我实现了一个延迟锁定列表,它支持以下方法:boolean add(T项)、boolean remove(T项)、boolean contains(T项) 例如,添加方法: @Override public boolean add(T item) { int key = item.hashCode(); while(true){ Node pred = head; Node curr = pred.next; while(curr
@Override
public boolean add(T item) {
int key = item.hashCode();
while(true){
Node pred = head;
Node curr = pred.next;
while(curr.key < key) { pred = curr; curr = curr.next; }
pred.Lock.lock();
curr.Lock.lock();
try{
if(!pred.marked && !curr.marked && pred.next == curr){
if(curr.key == key){ return false; }
else{ Node insertMe = new Node(item); insertMe.next = curr; pred.next = insertMe; return true; }
}
} finally{ pred.Lock.unlock(); curr.Lock.unlock(); }
}
}
@覆盖
公共布尔加法(T项){
int key=item.hashCode();
while(true){
节点pred=头部;
节点curr=pred.next;
而(curr.key
其中节点对象有一个易失性布尔字段“标记”,默认设置为false。节点的锁是可重入的。标记为true的节点被认为已被remove方法删除。然而,当特定对象的监视器被特定线程持有时,为什么这甚至是相关的呢?换句话说,线程是否会在任何时候获取对象的锁,然后看到该节点被标记为已删除
编辑:
contains方法显然根本不锁定,但是标记节点的目的是为了能够进行contains检查吗?因此,是否有必要声明标记的字段为volatile
包含:
@Override
public boolean contains(T item) {
int key = item.hashCode();
while(true){
Node curr = head;
while(curr.key < key) { curr = curr.next; }
if(curr.key == key && !curr.marked) return true;
else return false;
}
}
@覆盖
公共布尔包含(T项){
int key=item.hashCode();
while(true){
节点电流=头部;
while(curr.key
删除:
@Override
public boolean remove(T item) {
int key = item.hashCode();
while(true){
Node pred = head;
Node curr = pred.next;
while(curr.key < key) { pred = curr; curr = curr.next; }
pred.Lock.lock();
curr.Lock.lock();
try{
if(!pred.marked && !curr.marked && pred.next == curr){
if(curr.key != key) { return false; }
else{
curr.marked = true;
pred.next = curr.next;
return true;
}
}
} finally{ pred.Lock.unlock(); curr.Lock.unlock(); }
}
}
@覆盖
公共布尔删除(T项){
int key=item.hashCode();
while(true){
节点pred=头部;
节点curr=pred.next;
而(curr.key
在您的案例中,标记为的是contains方法所必需的,而不是add方法所必需的。我的猜测是,add函数中标记为
-check的完全没有任何作用
标记的字段绝对必须是易变的,因为JIT保留重新排序代码的权利,如果这会带来好处的话。唯一的保证是,在对易失性字段进行任何操作之前的任何代码都已在易失性字段更改之前(发生在关系之前)完全执行
因此,在上面的代码中,如果字段不是易失性的,JIT可以决定在调用“contains”的线程中创建标记为
的线程本地副本,然后检查标记为
的的本地版本,但此时可能已经过时。在这种情况下,同时运行包含
和删除
可能导致竞态条件
作为旁注,应该提到的是,来自并发包的集合解决了所有这些问题,更加优雅(并且没有bug),因此在所有情况下都应该是首选。如果复制标准库中已经存在的类,通常实现得更糟。整个过程只是为了练习和更好地理解并发性,不过还是要感谢旁注。标记的解释似乎是正确的,但您确定添加了吗?例如,如果线程t1遍历列表,现在有一个本地pred和curr及其引用,那么在这个add t1线程可以锁定pred和curr之前,t2移除了t1的pred仍然有引用的节点。如果PRID现在没有被标记,T1不会检查Curr.KyK=键并考虑检查是否正确,尽管T1的PRID引用的节点已经被删除了。但是,在多线程中,包含的请求总是在收到答复时过期。但是,它不会导致线程问题,因此它不是不正确的,只是也不是很有帮助。需要在add
方法中使用标记的check,因为该方法在获取锁之前迭代到节点,因此,此时可能没有完成并发运行的删除操作。在这种情况下,add
将保存对节点的引用,该节点在拥有锁时已被删除。由于这是一种非常罕见的情况,代码只需解锁并重复整个操作即可解决此问题。