Structure vs timing
GoF patterns mostly clarify structure; concurrency patterns clarify what happens when work overlaps.
Many teams learn GoF patterns first and then approach concurrency as if it were just an API layer. In reality, concurrency patterns answer different questions. Strategy, Adapter, and Facade clarify variation and structure. Concurrency patterns clarify ownership, work scheduling, communication, contention, and safe state change across overlapping execution.
A Java team can have elegant object structure and still fail under load because requests pile up on shared state or blocking IO. Knowing how concurrency patterns differ from classic design patterns helps teams avoid expecting the wrong tool to solve the wrong class of problem.
Classic design patterns often shape static structure and object collaboration. Concurrency patterns shape dynamic behavior under time pressure: when work runs, who owns mutable state, how callers coordinate with workers, and how bounded resources are protected. Both belong to design. They simply operate under different stresses.
A useful mental model is this: GoF patterns help code stay understandable as structure grows. Concurrency patterns help code stay correct and resilient as overlapping execution grows.
GoF patterns mostly clarify structure; concurrency patterns clarify what happens when work overlaps.
Strategy can choose an algorithm, but it will not tell you who owns the state that algorithm touches.
Queues, monitors, reactors, and pools are architecture choices around execution, not just libraries.
| Question | Classic design pattern lens | Concurrency pattern lens |
|---|---|---|
| How do responsibilities collaborate? | Use Strategy, Facade, Adapter, Observer, and others | Ask whether collaboration is synchronous, queued, evented, or isolated by worker ownership |
| How do we protect mutable state? | Usually not the primary concern | Use monitors, message passing, confinement, or immutability |
| How is capacity managed? | Rarely explicit | Thread pools, bounded queues, reactor loops, and backpressure matter directly |
| What fails under load? | Structural coupling and responsibility drift | Races, deadlocks, starvation, queue blowups, timeout cascades |
package org.javaomnibus.notifications;
public final class NotificationDispatcher {
private final Executor executor;
private final NotificationSender sender;
public NotificationDispatcher(Executor executor, NotificationSender sender) {
this.executor = executor;
this.sender = sender;
}
public void dispatch(Notification notification) {
executor.execute(() -> sender.send(notification));
}
}