有这样一个题目: 实现一个无锁的Stack,并写一段测试代码(多线程访问),证明这个Stack是线程安全的。
分析:
出于节省内存空间的考虑,使用链式的存储结构来实现。
实现该堆栈的思想为:
1、压入一个新节点时,将旧结点存入新结点中。弹出时将顶部节点中存入的上一节点取出并将其设为站定。
2、使用无锁的实现类AtomicReference作为安全的无锁局部变量对元素进行暂存。
以下为实现代码,具体解释参见注释。
static LockFreeStack<Object> stack = new LockFreeStack<>();
@Test
public void test01() throws Exception {
// 实现一个无锁的Stack,并写一段测试代码(多线程访问),证明这个Stack是线程安全的。给出程序以及运行的截图。
Push [] pushs = new Push[10];
Pop [] pop = new Pop[10];
for(int i = 0; i < 10; i++) {
pushs[i] = new Push();
pop[i] = new Pop();
}
for(int i = 0; i < 10; i++) {
pushs[i].start();
pop[i].start();
}
for(int i = 0; i < 10; i++) {
pushs[i].join();
pop[i].join();
}
}
static class Push extends Thread {
@Override
public void run() {
for(;;) {
stack.push("Random->"+Math.random());
}
}
}
static class Pop extends Thread {
@Override
public void run() {
for(;;) {
System.out.println("已出栈:" + stack.pop());
}
}
}
static class LockFreeStack<V> {
private AtomicReference<Node<V>> top = new AtomicReference<>();
public LockFreeStack() {
}
public void push(V o) {
Node<V> nextValue = new Node<V>(o, null);
Node<V> oldValue = null;
do {
oldValue = top.get();
nextValue.setNext(oldValue); //新节点存入旧节点
} while (!top.compareAndSet(oldValue, nextValue));
}
public V pop() {
Node<V> oldValue = null;
Node<V> nextValue = null;
do {
oldValue = top.get();
if (oldValue == null) {
continue;//oldValue为空则栈内是空的
}
nextValue = oldValue.getNext();
//while中oldValue == null 是指在空栈的情况下重试
} while (oldValue == null || !top.compareAndSet(oldValue, nextValue));
return oldValue.getValue();
}
class Node<T> {
private T value;
private Node<T> next;
public Node(T value, Node<T> next) {
super();
this.value = value;
this.next = next;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public Node<T> getNext() {
return next;
}
public void setNext(Node<T> next) {
this.next = next;
}
}
}