Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java CopyOnWriteArraySet: publish against a stable listener snapshot

Last updated: 5 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

CopyOnWriteArraySet provides snapshot iterators and copies its backing array on writes. It fits small listener registries with frequent reads and infrequent changes.

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

Java
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
concurrent collections
copyonwritearrayset-listener-snapshot
Storage details