首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >get方法上的Java锁获取

get方法上的Java锁获取
EN

Stack Overflow用户
提问于 2014-03-12 17:41:22
回答 3查看 185关注 0票数 3

我最近研究了Java中的一些并发类,比如PriorityBlockingQueue,下面是相关的代码片段:

代码语言:javascript
复制
public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    E result;
    try {
        result = size > 0 ? (E) queue[0] : null;
    } finally {
        lock.unlock();
    }
    return result;
}

让我们考虑重写一下(出于这个问题的目的),假设队列要么是set,要么是null,因此就变成了简单的return queue[0]。现在,我不确定在这种情况下获取锁是否真的有必要...由于一些边缘情况编译器的优化,它在没有锁的情况下是否是非线程安全的?

EN

回答 3

Stack Overflow用户

发布于 2014-03-12 17:49:09

锁定是为了确保与调用PriorityBlockingQueue上的方法的时间顺序相关的外部一致性。

假设thread-offerthread-peek调用peek之前调用offer -锁确保thread-peek被阻塞,直到thread-offer完成,此时thread-offer添加的值可以由thread-peak返回-日志可能如下所示:

代码语言:javascript
复制
2013-03-12 10:40:00.000 [thread-offer] INFO offering value X
2013-03-12 10:40:00.001 [thread-peak] INFO peeking value
2013-03-12 10:40:00.002 [thread-offer] INFO offered value X
2013-03-12 10:40:00.003 [thread-peak] INFO peeked value X

另一种方法是,在peek方法没有锁定的情况下,允许返回null,尽管之前调用了offer -日志可能如下所示:

代码语言:javascript
复制
2013-03-12 10:50:00.000 [thread-offer] INFO offering value X
2013-03-12 10:50:00.001 [thread-peak] INFO peeking value
2013-03-12 10:50:00.002 [thread-peak] INFO peeked value null
2013-03-12 10:50:00.003 [thread-offer] INFO offered value X

这些日志有点做作,但希望能证明这一点,即操作应该是原子的。

票数 2
EN

Stack Overflow用户

发布于 2014-03-12 17:57:09

考虑一下同时调用remove()的另一个线程。如果peek()未锁定,则可能发生以下情况:

代码语言:javascript
复制
peeker              remover
------              -------
                    ret = queue[0]
return queue[0] <-- BUG
                    queue[0] <- queue[1]
                    return ret

您有一个不一致;因为窥探器可以与删除程序并发执行,所以它看不到删除操作的结果。它返回错误的结果。这意味着队列不再是线程安全的。

票数 1
EN

Stack Overflow用户

发布于 2014-03-12 18:38:10

您可以使用CopyOnWriteArrayList或查看该类的实现。在那里,如果修改了列表,则只能进行同步。但请注意,即使其他线程正在移除或添加元素,您也可以读取列表。您总是使用当前列表的副本。PriorityBlockingQueue将锁定所有公共方法。因此,如果你有很多线程,这可能会阻止线程有效地工作。

还要考虑这种实现(另请参阅AtomicInteger)。每个线程都可以与PriorityQueue的本地副本一起工作,因此您不再需要同步。AtomicReference并不像锁那样昂贵:

代码语言:javascript
复制
public class PriorityQueueUpdater {

private AtomicReference<PriorityQueue<String>> priorityQueueRef= new AtomicReference<>  (null);

public PriorityQueue<String> getPriorityQueueRef() {
    for (;;) {
        PriorityQueue<String> current = priorityQueueRef.get();
        PriorityQueue<String> next = priorityQueueRef.get();
        if (priorityQueueRef.compareAndSet(current, next))
            return current;
    }
}

    public void setPriorityQueue(PriorityQueue<String> priorityQueue) {
        this.priorityQueueRef.set(priorityQueue);
    }

}

PS:使用AtomicMarkableReference,你也可以用一个快照组合两个变量。在您的示例中,它将是大小和队列。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22347381

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档