A very naive array based circular buffer implementation,
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class SomeRingBuffer {
private volatile String[] holder;
private AtomicInteger cursor = new AtomicInteger(-1);
public SomeRingBuffer(int capacity) {
holder = new String[capacity];
}
public void add(String data) {
if (data == null) {
return; // do nothing
}
int updatedCursor = cursor.updateAndGet(i -> (i + 1) % holder.length);
holder[updatedCursor] = data;
holder = holder; // holder is marked volatile, intentional write barrier
}
public List<String> getLastN(int n) {
if (n < 1 || n > holder.length) {
n = holder.length;
}
List<String> result = new ArrayList<>(n);
int current = cursor.get();
while (n > 0) {
result.add(holder[current--]);
if (current < 0) {
current = holder.length - 1;
}
n--;
}
return result;
}
}
It is supposed to be non-blocking and thread safe. Synchronisation is achieved using AtomicInteger, while memory visibility is ensured via volatile r/w.
Is there any problem from concurrency point of view? As we are writing to array elements, is memory visibility here guaranteed among the threads?
holderdoesn't guarantee which slots of the array are safe to read afterwards.addandholderwrite already happened,getLastNwill see the array slot properly written and the object itself properly published.addandgetLastN, all bets are off.holder = holder;had an effect, is just wrong. Avolatilewrite establishes a happens before relationship to subsequent reads but since all reads see the same value (as the reference doesn’t change), there is no way to tell whether a read is subsequent. The other thread could read the array reference before the writing threat performed the redundant write, hence, there is no happens before relationship.