CopyOnWriteArraySet provides snapshot iterators and copies its backing array on writes. It fits small listener registries with frequent reads and infrequent changes.
Java CopyOnWriteArraySet: publish against a stable listener snapshot
Operational contract
A receipt listener added during one publication is absent from that publication's iterator and can participate in the next one. Removing a listener likewise does not retroactively remove it from an iterator already obtained. Duplicate listeners are judged by equals, not object identity. Callbacks run on the publisher's thread; one slow or failing listener can delay or abort later callbacks. The sample leaves callback failure visible so the service can set an explicit isolation policy.
Failure case
A listener registers an audit listener while handling receipt 47. The audit listener does not receive 47 from that in-progress publication, but it can receive receipt 82 afterward.
Java code
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;
public class ReceiptListenerRegistry {
public interface Listener { void received(long receiptId); }
private final CopyOnWriteArraySet<Listener> listeners = new CopyOnWriteArraySet<>();
public boolean add(Listener listener) {
return listeners.add(Objects.requireNonNull(listener));
}
public boolean remove(Listener listener) {
return listeners.remove(listener);
}
public void publish(long receiptId) {
for (Listener listener : listeners) listener.received(receiptId);
}
}Performance and ownership cost
Publishing to L listeners is O(L) callback dispatch without copying for each read. Adding or removing is O(L) search plus an O(L) array copy on change, with a temporary second array. This is expensive for high-churn large sets.
Common Mistakes
- Do not expect a newly added listener to join an iteration already in progress.
- Do not treat callbacks as asynchronous or isolated from each other's failures.
- Do not choose copy-on-write for frequent writes and large registries without measuring allocation.
Connected lessons
- Java CopyOnWriteArrayList: dispatch against a listener snapshot
- Java ConcurrentHashMap: atomic updates and weakly consistent reads
- Java LinkedBlockingDeque: bound urgent and normal work in one queue
- Java PriorityBlockingQueue: add an admission limit to an unbounded priority queue
- Java specialized collections quiz
- Advanced Java
