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

Java CopyOnWriteArrayList: dispatch against a listener snapshot

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

CopyOnWriteArrayList publishes a fresh array when it changes. Iterators hold a snapshot, so registration changes do not disrupt an in-flight listener dispatch.

Operational contract

The registry fits a read-heavy system with infrequent listener changes. addIfAbsent avoids duplicate registration by equality. A dispatch gets the iterator view available when iteration begins; removing a listener during dispatch does not retract a callback already present in that snapshot. Callback failure is not rolled back and this sample deliberately lets it propagate to the caller. In a production event bus, failure isolation and delivery guarantees need their own policy.

Failure case

A warehouse adds one audit listener while 47 shipment notices are being dispatched. An in-progress traversal keeps the listeners it started with. The next dispatch can see the new registration. This makes iteration predictable, but it does not promise that a removal stops an already-started callback.

Java code

Java
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;

public class ShipmentNoticeListeners {
    private final CopyOnWriteArrayList<Consumer<String>> listeners = new CopyOnWriteArrayList<>();

    public void register(Consumer<String> listener) {
        listeners.addIfAbsent(listener);
    }

    public void remove(Consumer<String> listener) {
        listeners.remove(listener);
    }

    public void dispatch(String shipmentId) {
        for (Consumer<String> listener : listeners) listener.accept(shipmentId);
    }
}

Performance and ownership cost

A mutation copies the backing array, so it costs O(L) time and extra O(L) array memory for L listeners. A dispatch visits O(L) callbacks and its iterator can retain an old array until the traversal ends. High-churn registration is the wrong workload for this structure.

Common Mistakes

  • Do not choose copy-on-write storage when writes dominate.
  • Do not assume removal cancels callbacks already captured by an iterator.
  • Do not confuse snapshot iteration with transactional callback delivery.

Connected lessons

Continue with: Java CopyOnWriteArraySet: publish against a stable listener snapshot.

java
concurrent maps and snapshots
copyonwritearraylist-listener-registry
Storage details