💪 Writing a non-blocking ArrayList is a tricky but fun task — because arrays require index-based coordination, which is much harder to do without locks than a linked structure like a stack.
But let’s take it step by step and aim for a minimal lock-free-ish dynamic array structure — using atomic operations only.
✅ Goal
Create a non-blocking ArrayList-like class with:
add(T value)— append valueget(int index)— read valueremove(int index)— remove value at indexsize()— current element count
⚠️ Constraints & Assumptions
- We’ll use a fixed-capacity array for simplicity (no resizing).
- We’ll use
AtomicReferenceArray<T>to store values. - We’ll use
AtomicIntegerto track the current size.
This isn’t a full-blown dynamic ArrayList, but it demonstrates the principle of non-blocking coordination without synchronized.
🧱 Minimal Non-blocking ArrayList Implementation
import java.util.concurrent.atomic.*;
public class LockFreeArrayList<T> {
private final AtomicReferenceArray<T> array;
private final AtomicInteger size = new AtomicInteger(0);
public LockFreeArrayList(int capacity) {
array = new AtomicReferenceArray<>(capacity);
}
public boolean add(T value) {
int index;
do {
index = size.get();
if (index >= array.length()) return false; // Full
} while (!size.compareAndSet(index, index + 1));
array.set(index, value);
return true;
}
public T get(int index) {
if (index >= size.get()) throw new IndexOutOfBoundsException();
return array.get(index);
}
public T remove(int index) {
if (index >= size.get()) throw new IndexOutOfBoundsException();
T oldValue;
do {
oldValue = array.get(index);
if (oldValue == null) return null; // Already removed
} while (!array.compareAndSet(index, oldValue, null));
return oldValue;
}
public int size() {
return size.get();
}
}
✅ How It Works
add()usesAtomicIntegerto get the next available index without lockingget()is safe due toAtomicReferenceArrayremove()uses CAS to null out an index atomicallysize()is just a fastget()
🧪 Example Usage
public class Main {
public static void main(String[] args) {
LockFreeArrayList<String> list = new LockFreeArrayList<>(10);
list.add("A");
list.add("B");
System.out.println(list.get(1)); // B
System.out.println(list.remove(0)); // A
System.out.println(list.get(0)); // null (removed)
System.out.println(list.size()); // 2 (slots used)
}
}
🧠 Notes
- This is non-blocking, but not wait-free.
- It does not shift elements after removal (unlike
ArrayList) — it’s more like a sparse array. - No resizing logic included — that’s very complex to do lock-free.