Posts

Showing posts with the label Multithreading

Fork/Join Framework in Java — Parallelism with Work-Stealing

Image
The Fork/Join Framework (java.util.concurrent) is designed for parallelizing CPU-bound, divide-and-conquer tasks. It splits a big task into smaller subtasks (fork), runs them in parallel, and merges results (join). It's ideal for recursive algorithms (e.g., parallel sum, merge sort, matrix ops) and uses a work-stealing scheduler to balance load across worker threads. 1. When to use Fork/Join Large CPU-bound recursive tasks that can be divided into independent subtasks. Problems that follow divide-and-conquer pattern (array sum, parallel sort, tree algorithms). Not ideal for many short I/O-bound tasks — use ExecutorService for that. 2. Core Concepts ForkJoinPool — pool of worker threads optimized for Fork/Join tasks. ForkJoinTask — abstract base for tasks; two common subclasses: RecursiveTask<V> — returns a result. RecursiveAction — returns no result (void). Work-stealing — idle threads steal subtasks from busy threads...

Producer–Consumer Problem in Java Using BlockingQueue

Image
The Producer–Consumer problem is one of the most important multi-threading patterns in Java. Traditionally, developers used wait and notify to coordinate threads, but this approach is complex and error-prone. Modern Java provides a cleaner solution: BlockingQueue . 1. What is the Producer–Consumer Problem? Two types of threads share a common buffer: Producer → Generates data and puts it into the buffer Consumer → Retrieves data from the buffer and processes it The challenge: prevent producers from writing when the buffer is full and consumers from reading when the buffer is empty. 2. Problems with wait()/notify() The older approach required: Manual locking Complex condition handling Risk of deadlocks Hard to debug BlockingQueue solves these issues with built-in thread management. 3. Why BlockingQueue? BlockingQueue handles synchronization internally. Its key methods: put() – waits if the queue is full take() – waits if th...

ExecutorService, Future & CompletableFuture

Image
After understanding threads and multithreading basics, the next step in modern Java concurrency is learning how to manage threads efficiently. Manually creating threads is inefficient, difficult to control, and not scalable. Java solves these issues using ExecutorService, Future, and CompletableFuture. Why ExecutorService? ExecutorService provides a flexible framework for managing and reusing threads. It removes the need to manually create, start, and manage individual threads. Efficient resource utilization Thread reuse using thread pools Better performance Simpler asynchronous programming Avoids creating too many threads Types of Executor Services Java offers several ready-to-use executors based on usage: newSingleThreadExecutor: Executes tasks sequentially in one thread. newFixedThreadPool: Maintains a fixed number of threads. newCachedThreadPool: Creates threads as needed; good for short-lived tasks. newScheduledThreadPool: Executes delayed a...

Java Singleton Design Pattern: Implementation, Pitfalls & Fixes

Image
Master the Singleton design pattern in Java: standard approaches, multithreading concerns, serialization/reflection pitfalls, and fixes explained with code and diagrams. Introduction The Singleton pattern is a creational design pattern that ensures only one instance of a class exists in the JVM, and it provides a global point of access to that instance. It’s widely used for configurations, logging, caching, thread pools, and more. For Java interviews, Singleton is a common topic since it involves discussions on object lifecycle, concurrency, and design robustness . 1. Standard Singleton Implementation class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } This basic form ensures only one instance is created, but it is not thread-safe . Diagram (textual): Cl...

ConcurrentHashMap vs Synchronized HashMap in Java

Image
Introduction In multi-threaded Java applications, managing shared data safely and efficiently is crucial. Two common approaches are using Synchronized HashMap (via Collections.synchronizedMap(...) ) and ConcurrentHashMap . While both aim to provide thread-safety, their implementations and performance characteristics differ significantly. This post explores these differences in detail. What is a Synchronized HashMap? Created by wrapping a regular HashMap using: Map<K, V> syncMap = Collections.synchronizedMap(new HashMap<>()); Enforces synchronization at the object level , meaning only one thread can access the map at any time (read or write). Supports null keys and values since it relies on HashMap behavior. Iterators are fail-fast , throwing ConcurrentModificationException if the map is modified while iterating. Simpler implementation, suitable when concurrency is low or performance is not cri...

Understanding Multiple Threads in Java

Image
Introduction In Java programming, achieving parallel execution of tasks is made possible using multithreading. This blog explores how to create and run multiple threads concurrently, why using start() is critical, how scheduling works, and additional tips like using sleep() for clearer outputs. Demonstrating Multiple Threads Let's start with a simple example. We define two threads—one to say “Hi” and another to say “Hello”. class HiThread extends Thread {   public void run() {     for(int i = 0; i < 5; i++) {       System.out.println("Hi");       try { Thread.sleep(500); } catch(Exception e) {}     }   } } class HelloThread extends Thread {   public void run() {     for(int i = 0; i < 5; i++) {       System.out.println("Hello");       try { Thread.sleep...

Threads in Java

Image
Introduction In modern applications, responsiveness and efficiency are essential. Whether you’re building a desktop app, a game, or a backend server, you often need to perform multiple tasks simultaneously. This is where Threads in Java come into play. Threads allow you to run multiple tasks concurrently, making programs faster and more efficient. In this blog, we’ll explore: What threads are and why they are important How to create and manage threads in Java The thread lifecycle Common problems like race conditions How to ensure thread safety What is a Thread? A thread is the smallest unit of execution in a program. While a process represents an entire running application, threads are lightweight sub-tasks inside that process. 👉 Example: When you use WhatsApp, one thread handles message sending, another handles receiving, while another manages notifications — all at the same time. This is possible because of multithreading , which enables multiple threa...