I have written the following code (taken from the book "The Art of Multiprocessor Programming"):
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;
}
}
This seems to work if I test it with a small number of threads and operations, but it goes in deadlock every time I try to use 8 threads and 1000 operations per thread protected by this Lock. I inserted some prints to debug the code and an other thread that collects data on the working threads. I found that:
The test is done on a simple PriorityQueue.
The problem with this code is that it attempts to use ThreadLocal
to achieve thread confinement. However, due to linking the QNodes
in a linked list and manipulating instances through the next
reference and the tail
reference, it breaks that thread confinement, and in the absence of other synchronization mechanisms over the QNode
fields, visibility of changes is not guaranteed between threads.
Continuing to loop in line A is a consequence of seeing a stale value, despite the qnode.next.locked = false;
call in unlock()
.
Similarly, continuing to loop in line B is a consequence of seeing a stale value, despite the pred.next = qnode;
call in lock()
.
In both cases fields of a QNode
of another thread are mutated. In the former case qnode.next
, and in the latter pred
are QNodes
of the other thread.
如果在线程 pred.next
中运行set pred.next
,然后在不同步的qnode.next
在线程 qnode.next
中检查qnode.next ,则由于内存屏障 , 线程_2可能永远不会获得您在线程_1中设置的值。
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.