有這樣一個題目: 實現一個無鎖的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;
}
}
}