【发布时间】:2018-01-15 16:37:28
【问题描述】:
我编写了以下代码(摘自《多处理器编程的艺术》一书):
package Chapter7;
import java.util.concurrent.atomic.AtomicReference;
public class MCSLock implements Lock {
AtomicReference<QNode> tail;
ThreadLocal<QNode> myNode;
public MCSLock() {
tail = new AtomicReference<>(null);
myNode = new ThreadLocal<QNode>() {
@Override
protected QNode initialValue() {
return new QNode();
}
};
}
@Override
@SuppressWarnings("empty-statement")
public void lock() {
QNode qnode = myNode.get();
QNode pred = tail.getAndSet(qnode);
if (pred != null) {
qnode.locked = true;
pred.next = qnode;
while (qnode.locked); // line A
}
}
@Override
@SuppressWarnings("empty-statement")
public void unlock() {
QNode qnode = myNode.get();
if (qnode.next == null) {
if (tail.compareAndSet(qnode, null)) {
return;
}
while (qnode.next == null); // line B
}
qnode.next.locked = false;
qnode.next = null;
}
class QNode {
boolean locked = false;
QNode next = null;
}
}
如果我使用少量线程和操作对其进行测试,这似乎可行,但每次我尝试使用 8 个线程和受此锁保护的每个线程 1000 次操作时,它都会陷入死锁。我插入了一些打印件来调试代码和另一个收集工作线程数据的线程。我发现:
- 有时死锁在 A 线上,有时在 B 线上。
- 在第一种情况下,所有线程都在 A 行上循环。另一个正在收集数据的线程表明,线程循环的变量都为真,但只有一个,因此线程应该有可能取得进展!
- 在第二种情况下,除一个之外的所有线程都在 A 行循环,另一个在 B 行循环。数据收集线程显示 qnode.next 不为空。
- 没有饥饿线程,因为我检查了它们是否正在“活动等待”插入简单计数器(并且它们正在增加)。
测试是在一个简单的 PriorityQueue 上完成的。
【问题讨论】:
标签: java multithreading locking