在延迟锁定时,在持有特定监视器的同时修改对象的易失性布尔字段有什么用?

What is the deal with modifying the volatile boolean field of an object, while holding the specific monitor, when lazy locking?

我实现了一个惰性锁定列表,它支持以下方法:boolean add(T item)、boolean remove(T item)、boolean contains(T item)。

例如添加方法:

@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(); }
    }
}

其中 Node 对象有一个可变的布尔字段 "marked",默认设置为 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;
    }
}

删除:

    @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(); }
    }
}

在您的情况下,marked 是 contains 方法所必需的,而不是 add 方法所必需的。我的猜测是添加函数中的 marked-check 绝对没有任何作用。

marked 字段绝对必须是可变的,因为 JIT 保留重新排序代码的权利(如果这样做有好处)。唯一的保证是在更改 volatile 字段之前完全执行了对 volatile 字段的任何操作之前的任何代码(happens-before 关系)。

所以在上面的代码中,如果该字段不是易失性的,JIT 可以决定在调用 'contains' 的线程中制作 marked 的线程本地副本,然后检查该副本marked 的本地版本,但当时可能已经过时。在这种情况下 运行 containsremove 同时出现可能会导致竞争条件。

作为旁注,应该提到 Concurrent 包中的集合可以更优雅地解决所有这些问题(并且没有错误),因此在所有情况下都应该是首选。如果你复制标准库中已经存在的 class,你通常会更糟糕地实现它。