How to Create a Thread-Safe Bounded Buffer in Java? Example (original) (raw)

This is one of the common coding problems for Java developers which the interviewer asks to check both their coding skills as well to test their concurrency skills. A BoundedBuffer is a common data structure used in concurrent Java applications to pass data between threads. For example, you can use Bounded Buffer to implement the Producer-Consumer pattern in Java. Producer thread can put items for consumers to process and can wait if Buffer is full. This pattern allows Consumers to process items already placed in queue or buffer.

In this article, I will show you how to implement Bounded Buffer in Java. We will also use Generic to create a TypeSafe Bounded Buffer. A typesafe bounded buffer can hold any type of object and you can get it without typecasting.

BoundedBuffer Implementation in Java - Example

There are a couple of important things to note while implementing the Bounded Buffer data structure in Java. Like, you need a way to stop threads when the buffer is full. We also need to make the class thread-safe because multiple threads will work with our bounded buffer at the same time. For holding objects, we will just use Array.

How to Implement Bounded Buffer in Java? Example

1. Java Program to implement Bounded Buffer in Java

Here is our complete Java program to implement a thread-safe, Bounded Buffer in Java. I have not used any third-party library and just used core Java classes

public class Test {

public static void main(String args[]) {

    BoundedBuffer<String> buffer = new BoundedBuffer<String>(2);

    
    System.out.println("Is buffer empty? " + buffer.isEmpty());

    buffer.put("abc");
    buffer.put("def");

    
    System.out.println("Is buffer full? " + buffer.isFull());

    String value = buffer.take();
    System.out.println("element taken from buffer : " 
                            + value);
    value = buffer.take();
    System.out.println("another element taken from buffer : " 
                            + value);

    
    System.out.println("Is buffer empty again? " + buffer.isEmpty());
   
    
    buffer.take();
    System.out.println("This line will not print because main thread is blocked");

}

}

class BoundedBuffer {

private final Object[] buffer;
private int putpos, takepos, count;

public BoundedBuffer(int bound) {
    buffer = new Object[bound];
}


public synchronized void put(T object) {
    try {
        while (isFull()) {
            wait();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    doPut(object);
    notifyAll();
}


public synchronized T take() {
    try {
        while (isEmpty()) {
            wait();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    T element = doTake();
    notifyAll();
    return element;
}

public synchronized boolean isFull() {
    return count == buffer.length;
}

public synchronized boolean isEmpty() {
    return count == 0;
}

protected synchronized void doPut(T object) {
    buffer[putpos] = object;
    if (++putpos == buffer.length) {
        putpos = 0;
    }
    ++count;
}

protected synchronized T doTake() {
    T element = (T) buffer[takepos];
    if (++takepos == buffer.length) {
        takepos = 0;
    }
    --count;
    return element;
}

}

Output Is buffer empty? true Is buffer full? true element taken from buffer : abc another element taken from buffer : def Is buffer empty again? true

You can see that we first check if our Bounded Buffer is empty and then we put two items and then check again if the buffer is full because the size of our bounded buffer is two, which means it can only hold 2 items.

On the design side, I have not created any interface but if you want, you can also create an interface called Buffer which take(), put(), isEmpty(), isFull(), and size methods, and then implement the interface with a class called BoundedBuffer.

That's all about how to implement Bounded Buffer in Java. You can use this buffer to solve many classic concurrency problems like a producer-consumer problem. As a task for you, I suggest writing some JUnit tests for this class and check if they work properly in concurrent Java applications.

How will you create tests to check concurrency? Well, just use the Executor pattern to create multiple threads which can put the object into the buffer and multiple consumer threads which can retrieve the values from the buffer.

And, what is your favorite Java programming exercise? Producer Consumer problem, Palindrom, Prime Number, Binary Search, or this one?

Do let me know in comments.