Practicing multithreading and concurrency concepts from scratch. Covers thread lifecycle, synchronization, locks, classical problems, and the java.util.concurrent utilities.
- Creating threads by extending
Thread - Thread naming and priorities (
MIN_PRIORITY,MAX_PRIORITY) sleep,yield,interrupt- Daemon threads
- Race condition demo with a shared
Counter - Fixing race conditions using
synchronizedblocks - Using
jointo wait for threads to finish
supplyAsyncfor async non-blocking tasksthenApplyfor chaining transformationsorTimeoutandexceptionallyfor timeout handling and fallbackallOfandjointo wait on multiple futures
- Synchronizing multiple threads at a common barrier point
- Barrier action (runnable triggered when all threads arrive),
await,reset - Difference from
CountDownLatch— reusable and threads wait for each other
- Waiting for multiple services to finish using
Future.getvsCountDownLatch countDown,awaitwith timeout
- Raw threads vs
ExecutorService— performance comparison with thread pools Executors.newFixedThreadPool,newSingleThreadExecutor,newScheduledThreadPoolFuture—get,isDone,cancel,isCancelled, timeout withget(timeout, unit)CallablevsRunnablewith futuresinvokeAllwith timeout,invokeAnyScheduledExecutorService—schedule,scheduleAtFixedRate,scheduleWithFixedDelay
- Producer-Consumer using
waitandnotifyon a shared resource - Guarded blocks to coordinate between threads without busy waiting
ReentrantLock— basic usage, reentrancy with nested method callslock(),tryLock()with timeout,lockInterruptibly(),unlock()- Bank account withdrawal demo showing
tryLockwith timeout to avoid indefinite blocking - Fair vs unfair lock —
ReentrantLock(true)for FIFO ordering vs default unfair mode ReentrantReadWriteLock— concurrent reads with exclusive write locks
- Simulates a file abstraction with
read(offset, length)andwrite(offset, insert)operations - File content backed by
StringBuilderfor efficient mutable operations - Thread-safe using
ReentrantReadWriteLock— multiple concurrent readers, exclusive writer lock()called beforetryblock withunlock()infinallyto preventIllegalMonitorStateException- Debug logging per thread showing lock acquisition, release, before/after state on writes
- Classic three-thread-type problem: searchers, inserters, deleters on a shared list
- Searchers run fully concurrently with each other and with inserters (
readLock) - Inserters are mutually exclusive with each other (
ReentrantLockmutex) but allow parallel searches - Deleters are fully exclusive — block all searches and inserts (
writeLock) - Lock design:
readLockshared by search + insert;writeLockexclusive to delete;mutexserialises inserts - Debug logging per thread: lock acquire/release, operation type (APPEND, INSERT, DELETED), list size
DataStructureMaintests all thread types concurrently usingExecutorService+CountDownLatchstart gate
- Single bathroom shared by Democrats and Republicans with a capacity of 3
- No mixed occupancy allowed — bathroom must be pure Democrat or pure Republican at all times
ReentrantLock+Conditionfor mutual exclusion and waitingcanOccupy(party)encapsulates all entry logic: same-party capacity check, opposite-party empty-bathroom check- Anti-starvation:
sameOccupiedCountertracks consecutive same-party entries; capped at 5 when opposite party is waiting waitingRepublicanCount/waitingDemocratCounttrack actual waiters — cap only enforced when opposite party has real waiters, preventing indefinite blocking when opposite party is absent- Sleep (bathroom use) happens outside the lock; try-finally guarantees lock re-acquisition before cleanup
- Debug logging shows WAITING / ENTER / EXIT with full state: occupied count, current party, consecutive counter, both waiting counts
- Doubly linked list +
HashMapfor O(1) get and put - Sentinel
head/tailnodes eliminate null checks in add/remove ReentrantLock(fair) protects all operations atomically — check inside lock to prevent TOCTOU raceget: removes node and re-inserts at MRU (head) position under lockput: handles update, new entry, and LRU eviction (tail.prev) as three distinct cases
- Models backups as a DAG: each node has
dependencies(what it needs) anddependents(what needs it) - Each backup has
endTime(when backup completed) +retention(how long to keep it); expires atendTime + retention registerBackup: adds backup to map underwriteLockaddDependency(from, to): bidirectional edge linking underwriteLock; null-guardedfindRecoveryChain: post-order DFS withvisitedset underreadLock— handles multiple dependencies and shared ancestors; returns chain in application order (root → target)expireBackup: DP memoization (canDelete) checks expiry + all dependents recursively; entire compute + delete phase under singlewriteLockto prevent TOCTOU raceremoveNodecleans dependency edges in one direction only (dependencies'dependentslists) — dependents are guaranteed to also be deleted in the same pass- Lock strategy:
ReentrantReadWriteLock— concurrentfindRecoveryChainreads, exclusive writes for mutations
- Executes jobs respecting a DAG of dependencies — a job runs only after all its dependencies complete
- Kahn's algorithm initialized in constructor:
degreemap (unsatisfied deps per job) +readyQueueseeded with zero-degree jobs Jobholdsdependencies(what it needs) anddependents(what it unblocks); carries aRunnablefor actual workworker()returns aRunnable: waits onConditionwhenreadyQueueis empty, polls a job, executes outside lock, then decrements dependent degrees and signals when new jobs become readyremainingJobscounter drives termination — when it hits 0,condition.signalAll()wakes all workers to exit- Lock strategy: single
ReentrantLock+ConditionguardsreadyQueue,degree, andremainingJobs; job execution happens outside the lock so workers run truly in parallel maintests a 5-job DAG (1→3→5, 1+2→4→5) with 3 worker threads — Jobs 1 and 2 run in parallel first, then 3 and 4, then 5 last
- Supports one-time (ad-hoc), fixed-rate, and fixed-delay recurring task scheduling
- Three-layer architecture: user thread → scheduler thread → worker thread pool
PriorityQueue<ScheduledTask>(min-heap bynextExecutionTime) as the core data structureReentrantLock+Conditionfor thread-safe queue access and timed waiting (condition.await(delay))condition.signalAll()on enqueue wakes the scheduler thread immediately for earlier-than-current-head tasks- FIXED_RATE: next run = last scheduled time + period (clock-based, independent of task duration)
- FIXED_DELAY: next run = task finish time + delay (re-enqueued by worker thread after completion)
- Debug logging includes
HH:mm:ss.SSStimestamp + thread name at every lifecycle event: ENQUEUE, DISPATCH, EXECUTING, DONE, RE-ENQUEUE
src/main/java/com/definit3/concurrency/
├── basic/ # Thread creation, lifecycle, priorities, daemon
├── synchronization/ # Shared state, race conditions, synchronized blocks
├── completablefuture/ # supplyAsync, thenApply, allOf, orTimeout, exceptionally
├── cyclicbarrier/ # CyclicBarrier, barrier action, reset
├── countdownlatch/ # CountDownLatch, await, countDown
├── executorframework/ # ExecutorService, Future, Callable, ScheduledExecutor
├── threadcommunication/ # wait, notify, producer-consumer
├── filereadwrite/ # Thread-safe file read/write with offset using ReentrantReadWriteLock
├── concurrentdatastructure/ # Searcher/Inserter/Deleter with ReadWriteLock + mutex
├── taskscheduler/ # Multi-threaded task scheduler: one-time, fixed-rate, fixed-delay
├── politicalbathroom/ # Political bathroom problem: categorical exclusion with anti-starvation
├── lrucache/ # Thread-safe LRU cache: doubly linked list + HashMap + ReentrantLock
├── backupsytem/ # Backup dependency graph: registerBackup, addDependency, findRecoveryChain, expireBackup
├── jobscheduler/ # DAG-based job scheduler: Kahn's algorithm, worker threads, ReentrantLock + Condition
└── explicit/
├── lock/ # ReentrantLock, tryLock, lockInterruptibly
├── lockfairness/ # Fair vs unfair lock ordering
└── readwritelock/ # ReentrantReadWriteLock, concurrent reads with exclusive writes