The short version: A Singleton ensures one shared instance of a class exists app-wide. Atomics let multiple threads safely update a shared value without any locking.
Singleton Pattern
What It Is
A design pattern that guarantees a class has exactly one instance for the lifetime of the application, and provides a global access point to it.
You probably know it as: global object, global instance, or shared instance.
The formal name comes from the Gang of Four (GoF) design patterns book (1994). The concept predates the name.
The Three Moving Parts
| Part | What It Does |
|---|---|
| Private constructor | Prevents external code from calling new directly |
| Static instance variable | The class holds a reference to its own single instance |
| Static access method | Returns the instance (creates it on first call if needed) |
Python Example
class DatabaseConnection:
_instance = None # Static variable: holds the single instance
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if not self._initialized: # Only runs once
self.connection = "Connected to DB"
self._initialized = True
# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True (same object)
print(db1.connection) # "Connected to DB"
JavaScript Example
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance; // Return existing instance
}
this.logs = [];
Logger.instance = this;
}
log(message) {
this.logs.push(message);
console.log(`[LOG]: ${message}`);
}
getHistory() {
return this.logs;
}
}
// Usage
const logger1 = new Logger();
const logger2 = new Logger();
logger1.log("App started");
logger2.log("User logged in");
console.log(logger1 === logger2); // true
console.log(logger1.getHistory()); // ["App started", "User logged in"]
In JavaScript/Python: You Might Already Be Doing This
Module-level exports are de facto singletons. A module is evaluated once; every file that imports it gets the same instance. No formal pattern needed.
// db.js: this object is a singleton by nature of the module system
const db = { connection: "Connected to DB" };
export default db;
Common Use Cases
- Logging: one logger writing to one file
- Configuration: loaded once, shared everywhere
- Caching: one shared cache across the app
- Connection pools: managing a fixed set of resources
Watch Out For
| Pitfall | Why It Hurts |
|---|---|
| Testing difficulty | Global state bleeds between tests, making them hard to isolate |
| Hidden dependencies | Callers implicitly depend on it without declaring it |
| Concurrency | Multi-threaded environments need thread-safe initialization (locks, synchronized) |
Modern alternative: Dependency injection gives you the same "one instance" behavior with explicit, testable wiring.
Atomic Operations
What They Are
Operations that execute as a single, indivisible step at the hardware level. No thread can observe a half-finished state. The operation either completes fully or not at all.
Used for: safely reading and updating shared values across threads without a lock.
The Problem They Solve
Thread A reads counter: 5
Thread B reads counter: 5 ← same value, before A writes back
Thread A writes: 6
Thread B writes: 6 <- overwrites A (one update is lost)
Correct answer: 7
This is a race condition. Both threads read stale data and clobber each other's write.
Atomics vs. Mutex: The Key Difference
| Mutex (Lock) | Atomic | |
|---|---|---|
| Mechanism | Threads take turns (lock/unlock) | CPU handles it in one hardware instruction |
| Overhead | Locking + waiting overhead | Near-zero overhead |
| Scope | Any block of code | Simple read-modify-write ops only |
| Analogy | Deli counter: take a number, wait your turn | Everyone walks up at once; the counter serializes each request instantly |
When Atomics Win
High-contention values (a counter or flag that many threads touch constantly) are where atomics shine. A mutex makes those threads queue up. An atomic skips the queue entirely.
The Catch: Scope Is Limited
Atomics handle simple, single-value operations:
- Increment / decrement
- Compare-and-swap (read, compare, conditionally write)
- Fetch-and-add
If you need to update multiple related values together as one logical unit, you still need a mutex. Atomics are a scalpel; a mutex is the general-purpose tool.
C++ Atomics: std::atomic<T>
Available since C++11 via <atomic>.
#include <atomic>
#include <thread>
std::atomic<int> counter{0};
void increment() {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1); // atomic, no race condition
// counter++ also works; shorthand for fetch_add(1)
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
// counter is guaranteed to be 2000
}
Common std::atomic operations:
| Operation | Method | Shorthand |
|---|---|---|
| Read | load() |
implicit cast |
| Write | store(val) |
= val |
| Add and return old value | fetch_add(n) |
+= |
| Subtract and return old value | fetch_sub(n) |
-= |
| Compare, then conditionally swap | compare_exchange_weak/strong() |
— |
| Swap and return old value | exchange(val) |
— |
Memory ordering -- std::atomic defaults to memory_order_seq_cst (sequential consistency: safest, slight perf cost). For fine-tuned performance you can specify a weaker order like memory_order_relaxed on operations where ordering guarantees aren't needed. This is an advanced topic; default ordering is correct for most use cases.
// Relaxed: only guarantees atomicity, not ordering relative to other ops
counter.fetch_add(1, std::memory_order_relaxed);
Other Languages (for reference)
| Language | Atomic Type | Example |
|---|---|---|
| Java | AtomicInteger |
counter.incrementAndGet() |
| Python | threading.Lock or queue.Queue |
counter += 1 is NOT safe despite the GIL (it compiles to multiple bytecodes); use threading.Lock or queue.Queue instead |
| JavaScript | Atomics (SharedArrayBuffer) |
Atomics.add(arr, 0, 1) |
Summary: Pick the Right Tool
| Situation | Use |
|---|---|
| One shared resource, needs single owner | Singleton |
| Simple counter / flag across threads | Atomic |
| Multiple values that must update together | Mutex |
| Single-threaded app | None of the above; relax |