There are two important concepts in multithreading environment.
- atomicity
- visibility
Volatile
solves the visibility problem but it does not deal with atomicity e.g. i++. Here i++ is not a single machine instruction rather it is three machine instructions.
- copy the value to register
- increment it
- place it back
AtomicInteger
, AtomicReference
are based on the Compare and swap instruction. CAS has three operands a memory location V on which to operate, the expected old value A, and the new value B. CAS atomically updates V to the new value B, but only if the value in V matches the expected old value A; otherwise it does nothing. In either case, it returns the value currently in V. This is used by JVM in AtomicInteger
, AtomicReference
and they call the function as compareAndSet()
if this functionality is not supported by underlying processor then JVM implements it by spin lock.
Set is atomic (it is not always correct) but compare and then set is not atomic. So when you have a requirement for this e.g. when the value is X then only change to Y so to do this atomically you need this kind of primitives you can use compareAndSet of AtomicInteger
, AtomicReference
e.g. atomicLong.compareAndSet(long expect, long update)
You can actually use this primitives to develop powerful datastrucutures like Concurrent stack.
import java.util.concurrent.atomic.AtomicReference;
public class MyConcurrentStack<T> {
private AtomicReference<Node> head = new AtomicReference<Node>();
public MyConcurrentStack() {
}
public void push(T t) {
if (t == null) {
return;
}
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;
}
/**
*
* @return null if no element present else return a element. it does not
* remove the element from the stack.
*/
public T peek() {
Node<T> n = head.get();
if (n == null) {
return null;
} else {
return n.data;
}
}
public boolean isEmpty() {
if (head.get() == null) {
return true;
}
return false;
}
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;
}
}
}