Skip to main content

Command Palette

Search for a command to run...

Java Concurrency & Multithreading

Updated
3 min readView as Markdown
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):

  1. NEW → Created but not started.

  2. RUNNABLE → Eligible to run, waiting for CPU scheduling.

  3. BLOCKED → Waiting to acquire a monitor lock.

  4. WAITING → Indefinite wait until notified.

  5. TIMED_WAITING → Waiting with timeout (e.g. sleep, join(timeout)).

  6. 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() (not run()) 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.