RingBuffer是環形緩衝區,支持讀和寫兩種操做,相似於循環隊列。在實現上,通常用數組存儲數據,同時設置雙指針head和tail,head指向隊首,tail指向隊尾。讀數據時,head++;寫數據時,tail++。顯然,RingBuffer不是線程安全的,須要對讀寫數據進行同步。然而,有一種特殊狀況:一個線程讀,一個線程寫,在這種狀況下能夠實現線程安全的無鎖RingBuffer,代碼以下:數組
public class RingBuffer<T> {
private volatile T[] elements;
private volatile int head;
private volatile int tail;
public RingBuffer(int capacity){
this.elements=(T[])new Object[capacity];
this.head=0;
this.tail=-1;
}
private boolean isEmpty(){
return tail+1==head;
}
private boolean isFull(){
return tail+1-elements.length==head;
}
public void push(T element) throws IllegalArgumentException{
if(isFull())
throw new IllegalArgumentException("full queue");
elements[(tail+1)%elements.length]=element;
tail++;
}
public T pop() throws IllegalArgumentException{
if(isEmpty())
throw new IllegalArgumentException("empty queue");
T element=elements[head%elements.length];
head++;
return element;
}
}
複製代碼
爲何上述代碼可以確保線程安全?能夠看到,建立RingBuffer後,只有寫線程修改tail,只有讀線程修改head,而且始終只有一個讀線程,一個寫線程,所以是沒有併發寫操做的。然而,因爲讀操做和寫操做都不是原子性的,有可能讀操做發生在寫操做的過程當中,寫操做發生在讀操做的過程當中。這樣會致使如下兩個問題:安全
可是,對於寫操做,咱們是先修改elements,再修改tail;對於讀操做,咱們是先修改elements,再修改head。所以上述兩個問題是不存在的,咱們說上述RingBuffer是線程安全的,而且是無鎖的,具備較高的性能。併發