Advanced Java October 06 ,2025

 

Producer-Consumer Problem Using Custom LinkedBlockingQueue in Java

Complete Step-by-Step Guide

Introduction

The Producer-Consumer problem is a classic example of a multithreading problem where one or more producer threads generate data and place it into a shared resource (a queue), and one or more consumer threads take data from the shared resource for processing.

The main challenges in this problem are:

  • Synchronization — to avoid race conditions.
  • Blocking — producers should wait when the queue is full, and consumers should wait when it’s empty.
  • Thread safety — to ensure consistent data access.

Java provides tools like BlockingQueue for this, but in this blog we’ll solve it using our CustomLinkedBlockingQueue from the previous blog, building a complete producer-consumer system.

Step-by-Step Implementation

Step 1 — Understand Requirements

We need:

  1. A thread-safe queue (CustomLinkedBlockingQueue) with capacity.
  2. Producer threads to add tasks to the queue.
  3. Consumer threads to take tasks from the queue.
  4. Proper synchronization and blocking behavior.

Step 2 — Define CustomLinkedBlockingQueue

We will use the CustomLinkedBlockingQueue from the previous blog as our shared resource.

If you missed it, here’s the class name for reference:

CustomLinkedBlockingQueue

This queue already supports:

  • put() — blocks when the queue is full.
  • take() — blocks when the queue is empty.

Step 3 — Create Producer Class

The producer will generate integers and put them into the queue.

class Producer implements Runnable {
    private final CustomLinkedBlockingQueue queue;

    public Producer(CustomLinkedBlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        int value = 1;
        while (true) {
            try {
                System.out.println(Thread.currentThread().getName() + " producing: " + value);
                queue.put(value++);
                Thread.sleep(500); // simulate time taken to produce
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

Step 4 — Create Consumer Class

The consumer will take integers from the queue and process them.

class Consumer implements Runnable {
    private final CustomLinkedBlockingQueue queue;

    public Consumer(CustomLinkedBlockingQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Integer value = queue.take();
                System.out.println(Thread.currentThread().getName() + " consuming: " + value);
                Thread.sleep(1000); // simulate time taken to consume
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

Step 5 — Create Main Class to Test Producer-Consumer

We create multiple producer and consumer threads.

public class ProducerConsumerTest {
    public static void main(String[] args) {
        CustomLinkedBlockingQueue queue = new CustomLinkedBlockingQueue<>(5);

        Thread producer1 = new Thread(new Producer(queue), "Producer-1");
        Thread producer2 = new Thread(new Producer(queue), "Producer-2");

        Thread consumer1 = new Thread(new Consumer(queue), "Consumer-1");
        Thread consumer2 = new Thread(new Consumer(queue), "Consumer-2");

        producer1.start();
        producer2.start();
        consumer1.start();
        consumer2.start();
    }
}

Step 6 — Expected Output

Producer-1 producing: 1
Producer-2 producing: 2
Consumer-1 consuming: 1
Producer-1 producing: 3
Consumer-2 consuming: 2
Producer-2 producing: 4
...

Explanation:

  • Producers and consumers work concurrently.
  • Producers wait if the queue is full.
  • Consumers wait if the queue is empty.
  • Thread-safe access ensures no data corruption.

Step 7 — Add Graceful Shutdown (Optional)

In real-world systems, we need to stop threads gracefully.

Add a shutdown flag in producer and consumer classes:

private volatile boolean running = true;

public void shutdown() {
    running = false;
}

Then in run() method:

while (running) {
    ...
}

In main:

producer1.shutdown();
consumer1.shutdown();

 

Complete code

// Node class
class Node<T> {
    T item;
    Node<T> next;

    Node(T item) {
        this.item = item;
        this.next = null;
    }
}

// Custom LinkedBlockingQueue implementation
class CustomLinkedBlockingQueue<T> {
    private Node<T> head;
    private Node<T> tail;
    private int size;
    private final int capacity;

    public CustomLinkedBlockingQueue(int capacity) {
        this.capacity = capacity;
        head = tail = new Node<>(null); // dummy node
    }

    // Add element (Producer)
    public synchronized void put(T item) throws InterruptedException {
        while (size == capacity) {
            wait(); // wait if queue is full
        }
        Node<T> newNode = new Node<>(item);
        tail.next = newNode;
        tail = newNode;
        size++;
        notifyAll(); // notify waiting consumers
    }

    // Remove element (Consumer)
    public synchronized T take() throws InterruptedException {
        while (size == 0) {
            wait(); // wait if queue is empty
        }
        Node<T> first = head.next;
        head.next = first.next;
        size--;
        if (size == 0) {
            tail = head; // reset tail when empty
        }
        notifyAll(); // notify waiting producers
        return first.item;
    }

    public synchronized int size() {
        return size;
    }
}

// Producer class
class Producer implements Runnable {
    private final CustomLinkedBlockingQueue<Integer> queue;

    public Producer(CustomLinkedBlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        int value = 1;
        while (true) {
            try {
                System.out.println(Thread.currentThread().getName() + " producing: " + value);
                queue.put(value++);
                Thread.sleep(500); // simulate production delay
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

// Consumer class
class Consumer implements Runnable {
    private final CustomLinkedBlockingQueue<Integer> queue;

    public Consumer(CustomLinkedBlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Integer value = queue.take();
                System.out.println(Thread.currentThread().getName() + " consuming: " + value);
                Thread.sleep(1000); // simulate consumption delay
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

// Main Test Class
public class ProducerConsumerTest {
    public static void main(String[] args) {
        CustomLinkedBlockingQueue<Integer> queue = new CustomLinkedBlockingQueue<>(5);

        Thread producer1 = new Thread(new Producer(queue), "Producer-1");
        Thread producer2 = new Thread(new Producer(queue), "Producer-2");

        Thread consumer1 = new Thread(new Consumer(queue), "Consumer-1");
        Thread consumer2 = new Thread(new Consumer(queue), "Consumer-2");

        producer1.start();
        producer2.start();
        consumer1.start();
        consumer2.start();
    }
}

 Explanation:

  • Two producers generate integers and insert them into the custom blocking queue.
  • Two consumers retrieve and process those integers.
  • Thread safety is achieved using synchronized, wait(), and notifyAll().
  • The queue blocks automatically when full or empty, ensuring balanced producer-consumer flow.

Real-World Applications

The producer-consumer pattern with a blocking queue is useful for:

  • Web server request handling.
  • Data processing pipelines.
  • Logging systems.
  • Task scheduling systems.

 Enhancements

After implementing the basic producer-consumer problem, you can enhance:

  • Multiple producer-consumer coordination using fairness.
  • Priority-based consumption using priority queues.
  • Timeout operations for put/take methods.
  • Monitoring and metrics for production-level systems.

Best Practices

While custom implementations help in learning, in real projects use Java’s built-in:

  • LinkedBlockingQueue
  • ArrayBlockingQueue
  • PriorityBlockingQueue

These are highly optimized, thread-safe, and avoid common concurrency pitfalls.

Summary

The producer-consumer problem is a fundamental concurrency problem that helps developers understand synchronization and inter-thread communication.
By implementing it with a custom LinkedBlockingQueue, we learned how to:

  • Ensure thread safety using synchronized blocks.
  • Use wait() and notifyAll() to block threads efficiently.
  • Maintain FIFO ordering in shared resources.
  • Balance producers and consumers so neither overwhelms the other.

This hands-on approach deepens your understanding of multithreading, blocking queues, and concurrency design patterns. Implementing your own producer-consumer system builds a strong foundation for working with advanced Java concurrency tools such as ExecutorService, CompletableFuture, and reactive frameworks.

 

Next Blog- Implementing a Custom Thread Pool Using LinkedBlockingQueue in Java

 

Sanjiv
0

You must logged in to post comments.

Related Blogs

Generics P...
Advanced Java August 08 ,2025

Generics Part- 2

Collection...
Advanced Java July 07 ,2025

Collections Framewor...

Mastering...
Advanced Java August 08 ,2025

Mastering Java Multi...

Annotation...
Advanced Java August 08 ,2025

Annotations

Java Multi...
Advanced Java August 08 ,2025

Java Multithreading...

Java Memor...
Advanced Java August 08 ,2025

Java Memory Manageme...

Java Lambd...
Advanced Java August 08 ,2025

Java Lambda Expressi...

Java Funct...
Advanced Java August 08 ,2025

Java Functional Inte...

Java Strea...
Advanced Java August 08 ,2025

Java Stream API

JDBC (Java...
Advanced Java August 08 ,2025

JDBC (Java Database...

JDBC (Java...
Advanced Java September 09 ,2025

JDBC (Java Database...

Annotation...
Advanced Java August 08 ,2025

Annotations

Generics
Advanced Java August 08 ,2025

Generics

Java I/O (...
Advanced Java August 08 ,2025

Java I/O (NIO)

Introducti...
Advanced Java September 09 ,2025

Introduction to Desi...

Design Pat...
Advanced Java September 09 ,2025

Design Patterns in J...

Other Prin...
Advanced Java September 09 ,2025

Other Principles Beh...

Creational...
Advanced Java September 09 ,2025

Creational Design Pa...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

Creational...
Advanced Java September 09 ,2025

Creational Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

In Creatio...
Advanced Java September 09 ,2025

In Creational Design...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Builder De...
Advanced Java September 09 ,2025

Builder Design Patte...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Structural...
Advanced Java September 09 ,2025

Structural Design Pa...

Design Pat...
Advanced Java September 09 ,2025

Design Patterns in J...

Chain of R...
Advanced Java September 09 ,2025

Chain of Responsibil...

Command De...
Advanced Java September 09 ,2025

Command Design Patte...

Interprete...
Advanced Java September 09 ,2025

Interpreter Design P...

Iterator D...
Advanced Java September 09 ,2025

Iterator Design Patt...

Mediator D...
Advanced Java September 09 ,2025

Mediator Design Patt...

Memento De...
Advanced Java September 09 ,2025

Memento Design Patte...

Observer D...
Advanced Java September 09 ,2025

Observer Design Patt...

State Desi...
Advanced Java September 09 ,2025

State Design Pattern...

Strategy D...
Advanced Java September 09 ,2025

Strategy Design Patt...

Template M...
Advanced Java September 09 ,2025

Template Method Desi...

Visitor De...
Advanced Java September 09 ,2025

Visitor Design Patte...

Prototype...
Advanced Java September 09 ,2025

Prototype Design Pat...

Java 8+ Fe...
Advanced Java October 10 ,2025

Java 8+ Features

SOLID Prin...
Advanced Java October 10 ,2025

SOLID Principles in...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

How Iterat...
Advanced Java October 10 ,2025

How Iterators Work i...

How Concur...
Advanced Java October 10 ,2025

How ConcurrentHashMa...

Comparable...
Advanced Java October 10 ,2025

Comparable vs Compar...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Semaphore...
Advanced Java October 10 ,2025

Semaphore in Java

ExecutorSe...
Advanced Java October 10 ,2025

ExecutorService in J...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Implementi...
Advanced Java October 10 ,2025

Implementing a Custo...

Busy Spin
Advanced Java October 10 ,2025

Busy Spin

Serializat...
Advanced Java October 10 ,2025

Serialization and De...

Segment Lo...
Advanced Java October 10 ,2025

Segment Locking in J...

Tree Bins...
Advanced Java October 10 ,2025

Tree Bins in Java

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Custom Imp...
Advanced Java October 10 ,2025

Custom Implementatio...

Get In Touch

G06, Kristal Olivine Bellandur near Bangalore Central Mall, Bangalore Karnataka, 560103

+91-8076082435

techiefreak87@gmail.com