Java Concurrency & Multithreading

Introduction
I’ve been diving deep into concurrency and multithreading in Java. Instead of keeping my notes hidden, I’m sharing them here to learn in public.
This blog covers:
Basics of processes, threads, and execution
Concurrency vs parallelism
Java thread lifecycle & APIs
Executor framework
Synchronization concepts
Real-world coding problems (factorials, image processing, download manager, etc.)
🔹 Program vs Process vs Thread
Program → Set of instructions.
Process → Running instance of a program with its own memory space (heap not shared).
Thread → Smallest unit of execution inside a process, shares memory with other threads of the same process.
🔹 Concurrency vs Parallelism
Concurrency → Multiple tasks making progress but not necessarily at the same instant (single core).
Parallelism → Multiple tasks making progress at the same time (multi-core).
Note: Multithreading can run even on a single core → concurrency, not parallelism.
🔹 Java Thread Lifecycle
States (Thread.State enum):
NEW → Created but not started.
RUNNABLE → Eligible to run, waiting for CPU scheduling.
BLOCKED → Waiting to acquire a monitor lock.
WAITING → Indefinite wait until notified.
TIMED_WAITING → Waiting with timeout (e.g.
sleep,join(timeout)).TERMINATED → Completed execution.
🔹 Thread Creation in Java
// Extending Thread
class NewThread extends Thread {
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
}
}
// Implementing Runnable (preferred)
class SimpleRunnable implements Runnable {
public void run() {
System.out.println("Running via Runnable");
}
}
// With Lambda
Thread t = new Thread(() -> System.out.println("Lambda Thread"));
t.start();
👉 Why Runnable over Thread?
Follows composition over inheritance.
More extensible & flexible.
Compatible with modern Java features like Lambdas.
🔹 Executor Framework
Abstraction over manual thread management.
Provides thread pools for efficient reuse.
Types:
newFixedThreadPool(n)newCachedThreadPool()newScheduledThreadPool(n)newSingleThreadExecutor()
Supports Runnable and Callable (returns result).
🔹 Coding Examples
✅ Factorial with Threads
✅ Multi-threaded Merge Sort
✅ Download Manager with ExecutorService
✅ Image Processing (divide array into quadrants, process with 4 threads)
✅ ScheduledExecutorService (run tasks every 5s)
These helped me move from theory → practice.
GitHub → https://github.com/shubhamsaraswat17/SoftwareDevelopment
🔹 Synchronization Example
class Count { int value = 0; }
class Adder implements Runnable {
private Count count;
public Adder(Count c) { this.count = c; }
public void run() { for (int i=1; i<=100; i++) count.value += i; }
}
class Subtractor implements Runnable {
private Count count;
public Subtractor(Count c) { this.count = c; }
public void run() { for (int i=1; i<=100; i++) count.value -= i; }
}
👉 Without synchronization, race conditions occur.
👉 With synchronized, we ensure safe updates.
🔹 Key Takeaways
Concurrency ≠ Parallelism.
Always use
start()(notrun()) to create new threads.Prefer Executor Framework for production-grade apps.
Runnable = fire-and-forget, Callable = returns results.
Synchronization is key to avoid data inconsistency.