Mutex: mutual exclusion
A mutex lets exactly one thread hold the lock at a time, protecting a critical section from simultaneous read-modify-write access. Every shared variable touched by more than one thread needs a mutex, or an atomic equivalent.
Core CS Concept
Threads, mutexes, race conditions, deadlocks, atomic operations, message passing, and async/await explained from first principles. Worked deadlock walkthrough showing thread A holds lock 1 needs lock 2 while thread B holds lock 2 needs lock 1, plus per-language primitives in Java, C++, Python, Go, JavaScript. Verified CS graduates from CMU, MIT, and Berkeley, starting at $20 per task.
What it means
Concurrency lets multiple computations make progress at the same time, by interleaving on one CPU or running in parallel on many. Coordination requires synchronization primitives (mutex, semaphore, condition variable, atomic) or message passing.
Java AtomicInteger, C++ std::atomic, Python threading.Lock, Go channels: every language exposes the same hardware primitives differently. The Python GIL makes threads useful for I/O concurrency but not CPU parallelism unless you reach for multiprocessing.
Primary example
// Race condition: 4 threads each increment 250000 times.
// Expected final value: 1000000. Actual: between 500000 and 1000000.
public class Race {
static int counter = 0;
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[4];
for (int i = 0; i < 4; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 250000; j++) counter++;
});
threads[i].start();
}
for (Thread t : threads) t.join();
System.out.println(counter); // < 1000000 due to lost updates
}
}
// Fix: use AtomicInteger.incrementAndGet() or synchronized block.
Four synchronization primitives
A mutex lets exactly one thread hold the lock at a time, protecting a critical section from simultaneous read-modify-write access. Every shared variable touched by more than one thread needs a mutex, or an atomic equivalent.
A condition variable lets a thread release a mutex and sleep until another thread signals a state change. Standard for producer-consumer queues: consumers wait on "not empty"; producers wait on "not full". Always wait in a while loop to handle spurious wakeups.
Hardware-supported instructions like compare-and-swap and fetch-and-add update a memory location indivisibly, no mutex required. Use atomics for counters, flags, and lock-free data structures. Memory ordering (acquire, release, seq_cst) controls how the operation interleaves with surrounding code.
Wrong way vs right way
import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread_a():
with lock1:
# ... do work ...
with lock2: # waits for B to release lock2
pass
def thread_b():
with lock2:
# ... do work ...
with lock1: # waits for A to release lock1
pass
# Both threads sleep forever. import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread_a():
with lock1:
with lock2:
pass
# Always acquire lock1 first, then lock2.
def thread_b_fixed():
with lock1:
with lock2:
pass
# No cycle in the wait-for graph; deadlock impossible. Cross-language
// Async/await for parallel HTTP requests in Node.js
async function fetchAll(urls) {
// Promise.all runs requests in parallel, awaits all results
const responses = await Promise.all(
urls.map(url => fetch(url))
);
const bodies = await Promise.all(
responses.map(r => r.text())
);
return bodies;
}
// Sequential version (slow): each await blocks the next
async function fetchAllSlow(urls) {
const results = [];
for (const url of urls) {
const r = await fetch(url);
results.push(await r.text());
}
return results;
}
// Difference for 10 URLs at 100ms each:
// fetchAll: ~100ms (all parallel)
// fetchAllSlow: ~1000ms (sequential) FAQ
Submit your assignment and get expert, pedagogical help within 12 hours. Every solution ships with line-by-line comments, complexity analysis, and unlimited revisions.
Get Concurrency Help