Java 简单无锁堆栈

Java 简单无锁堆栈,java,stack,lock-free,Java,Stack,Lock Free,最近发现了这样的java并发访问任务: 使用两种方法编写简单的无锁堆栈: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(Obj

最近发现了这样的java并发访问任务:

使用两种方法编写简单的无锁堆栈: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面试新手。

您可以使用方法put()插入元素,并使用方法drainTo(集合c)获取元素。然后阅读c结尾的元素。

您肯定从正确的方向开始,考虑使用Java的原子整数和原子函数。因此,这将是一个无锁堆栈,如:没有显式锁

然而,当并发访问时,这仍然是不正确的,并且演示这一点相对简单:假设push()线程在获取计数和将新元素添加到堆栈(data[c]=o)之间阻塞,同时出现一个pop()线程,获取更高的计数,然后弹出。。。什么?堆栈中该位置的内存中发生了什么,而不是对象o(因为它尚未插入)

这就是无锁、基于数组的堆栈的问题,理论上需要调整两件事,即特定单元格的计数和内容,但不能同时在原子上进行这两件事。我不知道有任何无锁数组支持的堆栈算法

尽管有链表支持的堆栈算法是无锁的,因为在这种情况下,您可以创建一个新节点,将内容分配给它,并且您只有一个操作可以原子地执行:更改顶部指针

如果你对这个论点感兴趣,最好的文学作品是Shavit和Herlihy的《多处理器编程的艺术》,它描述了许多不同的数据结构,包括无锁和基于锁的数据结构。虽然Maged Michael在第8页第4.2点中提到了“常规”无锁堆栈算法,但我现在找不到任何详细描述该算法的文章,我自己也做过; 导入java.util.concurrent.AtomicReference; 公共级无锁钉{ 公共静态void main(字符串…参数){ LFStack stack=新LFStack(); 对于(int i=0;i<10;i++){ 线程t=新线程(新随机堆栈使用(堆栈)); t、 setName(“我的堆栈线程”+i); t、 start(); } } 私有静态类堆栈{ 私有volatile AtomicReference head=新的AtomicReference(); 公共E peek(){ E有效载荷=零; Node=head.get(); 如果(node!=null){payload=node.payload;} 返回有效载荷; } 公共E-pop(){ E有效载荷; while(true){ Node oldHeadNode=head.get(); 如果(oldHeadNode==null){return null;} 有效载荷=head.get().payload; if(head.compareAndSet(oldHeadNode,oldHeadNode.next.get()){break;} //System.out.println(“重试”); } 返回有效载荷; } 公共空间推送(E){ 节点oldHeadNode=新节点(e); while(true){ Node oldRootNode=head.get(); 如果(oldRootNode!=null){oldHeadNode.next.set(oldRootNode);} if(head.compareAndSet(oldRootNode,oldHeadNode)){break;} //System.out.println(“重试”); } } } //要用作链接列表链=>=>=>null 私有静态类节点{ 私人电子有效载荷; 下一步是私有原子参考; 公共节点(E){ 有效载荷=e; next=新的原子引用(); } } 公共静态类RandomStackUse实现可运行{ 私有LF堆栈; private Random rand=new Random(); 公共随机堆栈使用(LFStack stack){this.stack=stack;} @凌驾 公开募捐{ 长计数器=0; while(true){ if(rand.nextInt()%3==0){ stack.push(String.valueOf(counter++)); //System.out.println(String.format(“%s push%d”,Thread.currentThread().getName(),counter)); } if(rand.nextInt()%3==1){ 字符串值=stack.pop(); //System.out.println(String.format(“%s pop%s”,Thread.currentThread().getName(),value)); } if(rand.nextInt()%3==2){ 字符串值=stack.peek(); //System.out.println(String.format(“%s peek%s”,Thread.currentThread().getName(),value)); } } } } }
非常感谢您提供完整的答案!这是一个不正确的示例。可能会发生以下情况:1)推送:c=count.incrementAndGet()2)弹出:c=count.get();3) pop:top=data[c]4)data[c]=o所以得到一个旧的或未初始化的值。这是无锁但不正确的/robustI认为这里有一个错误:
java节点oldHeadNode=head.get();如果(oldHeadNode==null){return null;}payload=head.get().payload
最好只调用一次
head.get()
,否则,如果在两次调用
head.get()
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
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));
            }
        }
    }
}
}