/** * A Stack. */ public interface Stack { /** * Removes the top element from this stack and returns it. * * @return the top element of this stack. * @throw Exception if this stack is empty. */ public T pop() throws Exception; /** * Pushes the given element onto this stack. * * @param the element to be pushed onto this stack. */ public void push(T element); } In each implementation of the above interface we will exploit the class Node. /** * A node of a linked list. */ public class Node { private T element; private Node next; /** * Initializes this node with the given element and next node. * * @param element the element stored in the node. * @pre. element != null * @param next the next node in the list. */ public Node(T element, Node next) { this.element = element; this.next = next; } /** * Returns the element of this node. * * @return the element of this node. */ public T getElement() { return this.element; } /** * Returns the next node. * * @return the next node. */ public Node getNext() { return this.next; } /** * Sets the next node to the given node. * * @param node the new next node. */ public void setNext(Node next) { this.next = next; } } In the first implementation, we simply lock the whole stack when we pop or push an element. public class First implements Stack { private Node top; public First() { this.top = null; } public synchronized T pop() throws Exception { if (this.top == null) { throw new Exception(); } else { T element = this.top.getElement(); this.top = this.top.getNext(); return element; } } public synchronized void push(T element) { this.top = new Node(element, this.top); } } In the second implementation, we lock the top node when we pop or push an element. public class Second implements Stack { private Node top; public Second() { this.top = null; } public T pop() throws Exception { Node node; synchronized (this.top) { if (this.top == null) { throw new Exception(); } else { node = this.top; this.top = this.top.getNext(); } } return node.getElement(); } public void push(T element) { Node node = new Node(element, null); synchronized (this.top) { node.setNext(this.top); this.top = node; } } } The third implementation does not use any locking. We exploit the class AtomicReference. import java.util.concurrent.atomic.*; public class Third implements Stack { private volatile AtomicReference> top; public Third() { this.top = new AtomicReference>(); } public T pop() throws Exception { Node node; do { node = this.top.get(); if (node == null) { throw new Exception(); } } while (!this.top.compareAndSet(node, node.getNext())); return node.getElement(); } public void push(T element) { Node node = new Node(element, null); do { node.setNext(this.top.get()); } while (!this.top.compareAndSet(node.getNext(), node)); } } Also the fourth implementation does not use any locking. This time we exploit the class AtomicReferenceFieldUpdater. import java.util.concurrent.atomic.*; public class Fourth implements Stack { private volatile Node top; private static final AtomicReferenceFieldUpdater topUpdater = AtomicReferenceFieldUpdater.newUpdater(Fourth.class, Node.class, "top"); public Fourth() { this.top = null; } public T pop() throws Exception { Node node; do { node = this.top; if (node == null) { throw new Exception(); } } while (Fourth.topUpdater.compareAndSet(this, node, node.getNext())); return node.getElement(); } public void push(T element) { Node node = new Node(element, null); do { node.setNext(this.top); } while (Fourth.topUpdater.compareAndSet(this, node.getNext(), node)); } }