[英]Simple Lock-Free Stack
最近發現了這樣的java並發面試任務:
使用兩種方法編寫簡單的無鎖 Stack:push 和 pop。
我提出了意見:
import java.util.concurrent.atomic.AtomicInteger;
public class Stack {
private AtomicInteger count = new AtomicInteger(-1);
private Object[] data = new Object[1000];
public void push(Object o) {
int c = count.incrementAndGet();
data[c] = o;
}
public Object pop() {
Object top;
int c;
while (true) {
c = count.get();
if (c == -1) return null;
top = data[c];
if (count.compareAndSet(c, c-1))
return top;
}
}
}
它與預期的方法相似嗎? 或者“無鎖堆棧”意味着不同的東西? 請幫助一個java面試新手。
您肯定已經朝着正確的方向開始,考慮使用 Java 的原子整數和原子函數。 因此,這將是一個無鎖堆棧,如:沒有顯式鎖。
然而,並發訪問時它仍然不正確,並且證明這一點相對簡單:想象一下你的 push() 線程在獲取計數和將新元素添加到堆棧之間阻塞 (data[c] = o),並且在與此同時,一個 pop() 線程出現,獲得更高的計數,然后彈出......什么? 無論發生在堆棧中那個位置的內存中,而不是 Object o (因為它尚未插入)。
這就是無鎖、數組支持的堆棧的問題,理論上你需要調整兩件事,即特定單元格的計數和內容,並且不能同時以原子方式進行。 我不知道那里有任何無鎖數組支持的堆棧算法。
盡管有鏈表支持的堆棧算法是無鎖的,因為在這種情況下,您可以創建一個新節點,為其分配內容,並且您只有一個原子地執行的操作:更改頂部指針。
如果您對這個論點感興趣,最好的文學作品是 Shavit 和 Herlihy 的“多處理器編程藝術”,它描述了許多不同的數據結構,包括無鎖和基於鎖的。 我現在找不到任何詳細描述“通常”無鎖堆棧算法的論文,盡管 Maged Michael 在 他的 SMR 論文第 8 頁第 4.2 點中提到了它,而且我自己已經完成了 C99 實現。
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
public class LockFreeStack {
public static void main(String... args) {
LFStack<String> stack = new LFStack<String>();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(new RandomStackUse(stack));
t.setName("My stack thread " + i);
t.start();
}
}
private static class LFStack<E> {
private volatile AtomicReference<Node<E>> head = new AtomicReference<Node<E>>();
public E peek() {
E payload = null;
Node<E> node = head.get();
if (node != null) { payload = node.payload; }
return payload;
}
public E pop() {
E payload;
while (true) {
Node<E> oldHeadNode = head.get();
if (oldHeadNode == null) { return null; }
payload = head.get().payload;
if (head.compareAndSet(oldHeadNode, oldHeadNode.next.get())) { break; }
//System.out.println("Retry");
}
return payload;
}
public void push(E e) {
Node<E> oldHeadNode = new Node<E>(e);
while (true) {
Node<E> oldRootNode = head.get();
if (oldRootNode != null) { oldHeadNode.next.set(oldRootNode); }
if (head.compareAndSet(oldRootNode, oldHeadNode)) { break; }
//System.out.println("Retry");
}
}
}
//to be used as LinkedList chain <Node> => <Node> => <Node> => null
private static class Node<E> {
private E payload;
private AtomicReference<Node<E>> next;
public Node(E e) {
payload = e;
next = new AtomicReference<Node<E>>();
}
}
public static class RandomStackUse implements Runnable {
private LFStack<String> stack;
private Random rand = new Random();
public RandomStackUse(LFStack<String> stack) {this.stack = stack;}
@Override
public void run() {
long counter = 0;
while (true) {
if (rand.nextInt() % 3 == 0) {
stack.push(String.valueOf(counter++));
//System.out.println(String.format("%s pushed %d", Thread.currentThread().getName(), counter));
}
if (rand.nextInt() % 3 == 1) {
String value = stack.pop();
//System.out.println(String.format("%s pop %s", Thread.currentThread().getName(), value));
}
if (rand.nextInt() % 3 == 2) {
String value = stack.peek();
//System.out.println(String.format("%s peek %s", Thread.currentThread().getName(), value));
}
}
}
}
}
public class MyConcurrentStack<T>
{
private AtomicReference<Node> head = new AtomicReference<Node>();
public MyConcurrentStack()
{
}
public void push(T t)
{
Node<T> n = new Node<T>(t);
Node<T> current;
do
{
current = head.get();
n.setNext(current);
}while(!head.compareAndSet(current, n));
}
public T pop()
{
Node<T> currentHead = null;
Node<T> futureHead = null;
do
{
currentHead = head.get();
if(currentHead == null)
{
return null;
}
futureHead = currentHead.next;
}while(!head.compareAndSet(currentHead, futureHead));
return currentHead.data;
}
public T peek()
{
Node<T> n = head.get();
if(n==null)
{
return null;
}
else
{
return n.data;
}
}
private static class Node<T>
{
private final T data;
private Node<T> next;
private Node(T data)
{
this.data = data;
}
private void setNext(Node next)
{
this.next = next;
}
}
public static void main(String[] args)
{
MyConcurrentStack m = new MyConcurrentStack();
m.push(12);
m.push(13);
m.push(15);
System.out.println(m.pop());
System.out.println(m.pop());
System.out.println(m.pop());
System.out.println(m.pop());
}
}
該代碼是不言自明的。 如果有人需要解釋,請告訴我。 堆棧如下圖所示:
... ... ...
| |-->| | -->| |
... ... ...
^
|
current head
您可以使用BlockingQueue使用方法 put() 插入元素並使用方法 drainTo(Collection c) 獲取元素。 然后從c的末尾讀取元素。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.