我最近研究了Java中的一些并发类,比如PriorityBlockingQueue,下面是相关的代码片段:
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]。现在,我不确定在这种情况下获取锁是否真的有必要...由于一些边缘情况编译器的优化,它在没有锁的情况下是否是非线程安全的?
发布于 2014-03-12 17:49:09
锁定是为了确保与调用PriorityBlockingQueue上的方法的时间顺序相关的外部一致性。
假设thread-offer在thread-peek调用peek之前调用offer -锁确保thread-peek被阻塞,直到thread-offer完成,此时thread-offer添加的值可以由thread-peak返回-日志可能如下所示:
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 -日志可能如下所示:
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这些日志有点做作,但希望能证明这一点,即操作应该是原子的。
发布于 2014-03-12 17:57:09
考虑一下同时调用remove()的另一个线程。如果peek()未锁定,则可能发生以下情况:
peeker remover
------ -------
ret = queue[0]
return queue[0] <-- BUG
queue[0] <- queue[1]
return ret您有一个不一致;因为窥探器可以与删除程序并发执行,所以它看不到删除操作的结果。它返回错误的结果。这意味着队列不再是线程安全的。
发布于 2014-03-12 18:38:10
您可以使用CopyOnWriteArrayList或查看该类的实现。在那里,如果修改了列表,则只能进行同步。但请注意,即使其他线程正在移除或添加元素,您也可以读取列表。您总是使用当前列表的副本。PriorityBlockingQueue将锁定所有公共方法。因此,如果你有很多线程,这可能会阻止线程有效地工作。
还要考虑这种实现(另请参阅AtomicInteger)。每个线程都可以与PriorityQueue的本地副本一起工作,因此您不再需要同步。AtomicReference并不像锁那样昂贵:
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,你也可以用一个快照组合两个变量。在您的示例中,它将是大小和队列。
https://stackoverflow.com/questions/22347381
复制相似问题