Problems
Data race / race condition
AI-generated
A data race occurs when concurrent operations access the same memory, at least one operation writes, and the accesses are not ordered by the language's synchronization rules. A race condition is broader: correctness depends on uncontrolled timing or ordering even when every individual access is atomic.
Deadlock
2 or more tasks waiting for a shared resource, that must be free from the other, so none of them will get the resource -> indefinite blocking.
- Happens when following 4 happen simultaneously (Coffman’s conditions):
- Mutual exclusion. -> only one task allowed.
- Hold and wait condition. -> a task has mutex on a resource and is requesting mutex on another resource.
- No pre-emption. -> resource can only be released by the tasks that holds them.
- Circular wait. -> (1 —> 2 —> 3 —> … n —> 1)
Live lock
AI-generated
Two or more tasks keep reacting to one another and changing state, but no task makes useful progress. Unlike a deadlock, the tasks remain active.
Resource starvation
AI-generated
One task waits indefinitely because other tasks repeatedly receive the resource or scheduling opportunity first.
- Solution -> fair allocation algorithm.
- Fairness solutions to this problem takes additional overhead.
Priority Inversion
Low priority task holds the resource needed by the high priority task.
Models
Shared mutable state
AI-generated
- Synchronize/lock access - may lead to deadlocks
- Fix deadlocks by locking objects in a pre-defined order.
- Use atomic operations for simple state transitions; use locks or higher-level coordination when several values must change together.
- Accidental Non-determinism in thread execution (coz controlled by OS).
- Not evident from library/classes that they share mutable state.
Functional way (parallelism)
AI-generated
Pure functions and immutable values reduce coordination because concurrent tasks do not mutate the same state. They do not guarantee parallel speed-up or freedom from deadlock; futures can still wait cyclically, block a limited executor, or perform nondeterministic I/O.
- A future represents a result that may become available later.
- A promise is a writable handle used to complete a future once.
- Parallel work is useful only when the tasks are independent enough and the scheduling/communication overhead is smaller than the saved computation time.
Message passing and functional concurrency
AI-generated
- Clojure atoms apply an atomic function to one reference. The function may be retried, so it must not perform side effects.
- Clojure refs/STM coordinate changes to multiple references inside
dosync. - Clojure agents serialize asynchronous actions against one value;
awaitwaits for queued actions when a synchronous boundary is needed. - In the actor model, actors isolate state and communicate through messages. An actor handles one message at a time, but the system still needs supervision, mailbox limits, ordering assumptions, and failure handling.