|
Requirement |
Recommended |
|
Reuse worker threads |
ExecutorService |
|
Delayed result |
Future |
|
Async pipelines |
CompletableFuture |
|
Massive I/O concurrency |
Virtual Threads |
|
Per-thread context |
ThreadLocal |
|
Shared counter |
AtomicInteger / LongAdder |
|
Shared variable visibility |
volatile |
|
Critical section |
synchronized / ReentrantLock |
|
Read-heavy data |
ReadWriteLock / StampedLock |
|
Parallel CPU tasks |
ForkJoinPool |
|
Large collection processing |
Parallel Streams |
|
Wait for multiple tasks |
CountDownLatch |
|
Synchronize phases |
CyclicBarrier |
|
Limit concurrent access |
Semaphore |
|
Feature |
ArrayList |
Vector |
|
Introduced |
Java 1.2 |
Java 1.0 |
|
Thread Safe |
❌ No |
✅ Yes |
|
Synchronization |
None |
Every method synchronized |
|
Performance |
Faster |
Slower |
|
Growth |
50% increase |
Doubles by default (or configurable) |
|
Legacy |
No |
Yes |
|
Iterator |
Fail-fast |
Iterator is fail-fast; Enumeration is not fail-fast |
|
Preferred |
✅ Yes |
Rarely |
|
Operation |
ArrayList |
Vector |
|
get() |
O(1) |
O(1) |
|
set() |
O(1) |
O(1) |
|
add(end) |
O(1) amortized |
O(1) amortized |
|
add(index) |
O(n) |
O(n) |
|
remove |
O(n) |
O(n) |
|
contains |
O(n) |
O(n) |
Single-threaded or externally synchronized code: ArrayList
Read-heavy concurrent workloads: CopyOnWriteArrayList
General concurrent applications: Choose an appropriate collection from java.util.concurrent
Avoid Vector unless you're maintaining legacy code.
Thread → Worker
Multithreading → Multiple workers
Batch processing. Multiple threads share huge workload
Concurrency → Workers taking turns efficiently
Only one server in the hotel serving for 4 tables efficiently
Context switching heavily in case of single processor
Parallelism → Workers working at the same time
4 servers serving 4 tables. 1 server per table
example : 8 core cpu
Thread-safe → Workers don't corrupt shared data
Banking transactions – withdrawal –
only one thread is allowed to withdraw and update the balance, not multiple threads in parallel
Comparison
|
Feature |
Concurrency |
Parallelism |
|
CPU cores |
1 or more |
Usually multiple |
|
Executes simultaneously |
Not necessarily |
Yes |
|
Context switching |
Yes |
Minimal |
|
Goal |
Better responsiveness |
Faster execution |
|
Example |
Web server |
Matrix multiplication |
balance = 1000
Two threads
Thread A
withdraw(100)
Thread B
withdraw(200)
Both read
1000
A writes
900
B writes
800
Final balance
800
Correct answer should be
700
This is called a race condition.
public synchronized void withdraw(int amount) {
balance -= amount;
}
// ReentrantLock is more flexible than synchronized
Now
Thread A
LOCK
withdraw
UNLOCK
↓
Thread B
LOCK
withdraw
UNLOCK
Result
700
Correct.
In multithreading
Prefer ConcurrentHashMap instead of HashMap
CopyOnWriteArrayList instead of ArrayList
├── Thread
├── Runnable
├── Callable
├── Executor
├── ExecutorService
│ ├── FixedThreadPool
│ ├── CachedThreadPool
│ ├── SingleThreadExecutor
│ ├── ScheduledExecutorService
│ └── WorkStealingPool
│
├── Future
├── CompletableFuture
├── ForkJoinPool
├── Parallel Streams
├── Virtual Threads (Java 21+)
│
├── synchronized
├── volatile
├── Lock
├── ReadWriteLock
├── StampedLock
│
├── AtomicInteger
├── LongAdder
├── ConcurrentHashMap
├── ThreadLocal
│
├── Semaphore
├── CountDownLatch
├── CyclicBarrier
├── Phaser
└── BlockingQueue
Danger. Can create thousands of threads.
Good for Short-lived tasks.
Useful for
scheduleAtFixedRate()
scheduleWithFixedDelay()
Run every 10 seconds
Useful for
Idle threads steal work from busy threads.
Excellent for
Represents a result that will be available later.
future.get()
future.cancel()
future.isDone()
future.isCancelled()
Problem
future.get()
Blocks the current thread.
Modern replacement for Future.
Supports
supplyAsync()
runAsync()
thenApply()
thenCompose()
thenCombine()
exceptionally()
allOf()
anyOf()
Without volatile
Each thread may have its own cached copy.
Thread B might never see updates made by Thread A.
With volatile
volatile int count
Every read goes to main memory.
Every write updates main memory immediately.
Guarantees
· Visibility
· Memory ordering
It does not guarantee atomicity.
count++
is still not thread-safe because it consists of:
1. Read
2. Increment
3. Write
Use AtomicInteger instead.
volatile vs synchronized
|
volatile |
synchronized |
|
Visibility |
Visibility + Mutual Exclusion |
|
No locking |
Locking |
|
Faster |
Slower |
|
No atomicity |
Atomic operations |
|
Single variable |
Multiple statements |
volatile guarantees visibility, but AtomicInteger guarantees visibility and atomicity.
Use it for state flags or configuration values that are written by one thread and read by many threads.
Examples
volatile boolean shutdown;
volatile boolean running;
volatile Configuration config;
You are not performing compound updates like increment or decrement.
Use it whenever multiple threads modify a shared numeric value.
Examples
AtomicInteger counter;
AtomicLong requestCount;
AtomicReference<Customer>;
volatile only guarantees that every thread sees the latest value. It does not make compound operations such as count++, balance -= amount, or if (count > 0) count-- atomic.
Can I replace every volatile with an Atomic class?
No.
For simple state flags (running, shutdown, initialized) or references where you only need visibility, volatile is simpler, lighter, and more readable.
Otherwise it would be overkill