Самым простым и классическим решением является ограниченный кольцевой буфер, который переопределяет самые старые элементы.
Реализация довольно проста.Вам нужен один AtomicInteger / Long для index + AtomicReferenceArray, и у вас есть свободный от блокировки стек общего назначения только с 2 методами offer/poll
, без size()
.Большинство параллельных / не блокируемых структур имеют трудности с размером ().Не перекрывающий стек может иметь O (1), но с распределением на путе.
Что-то вроде:
package bestsss.util;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class ConcurrentArrayStack<E> extends AtomicReferenceArray<E>{
//easy to extend and avoid indirections,
//feel free to contain the ConcurrentArrayStack if feel purist
final AtomicLong index = new AtomicLong(-1);
public ConcurrentArrayStack(int length) {
super(length); //returns
}
/**
* @param e the element to offer into the stack
* @return the previously evicted element
*/
public E offer(E e){
for (;;){
long i = index.get();
//get the result, CAS expect before the claim
int idx = idx(i+1);
E result = get(idx);
if (!index.compareAndSet(i, i+1))//claim index spot
continue;
if (compareAndSet(idx, result, e)){
return result;
}
}
}
private int idx(long idx){//can/should use golden ratio to spread the index around and reduce false sharing
return (int)(idx%length());
}
public E poll(){
for (;;){
long i = index.get();
if (i==-1)
return null;
int idx = idx(i);
E result = get(idx);//get before the claim
if (!index.compareAndSet(i, i-1))//claim index spot
continue;
if (compareAndSet(idx, result, null)){
return result;
}
}
}
}
Последнее примечание: работа с модом стоит дорогомощность в 2 является предпочтительной, через &length()-1
(также защищает от длительного переполнения).