Producer-Consumer (Bounded Buffer)

Pacing the chefs so the waiters aren't overwhelmed.

The idea

In a concurrent system, one thread (Producer) creates data, and another thread (Consumer) processes it. They communicate via a queue. But what if the Producer generates 10,000 items a second, and the Consumer can only process 10? If the queue is infinite, the system will run out of RAM and crash. The solution is a Bounded Buffer: a queue with a maximum size limit. If the queue is full, the Producer goes to sleep until space opens up.

Step 1: The Bounded Buffer has a capacity of 3 items. It currently has 2.

How it works (Blocking Queues)

Almost every language standard library provides a Thread-Safe Bounded Queue (e.g., ArrayBlockingQueue in Java, Channels in Go). You don't have to write the sleep/wake logic yourself; the queue's put() and take() methods automatically block the calling thread when necessary.

# Go example: Channels are Bounded Buffers

// Create a buffer that holds exactly 3 items
jobs := make(chan string, 3)

// PRODUCER
func produce() {
    jobs <- "Job A" // Puts job in. 
    jobs <- "Job B"
    jobs <- "Job C"
    // Buffer is now FULL!
    jobs <- "Job D" // ⚠ Thread pauses here automatically until Consumer takes an item
}

// CONSUMER
func consume() {
    // ⚠ Thread pauses here automatically if Buffer is EMPTY
    job := <-jobs 
    process(job)
}

Cost

Bounded buffers apply natural Backpressure. If the system is overwhelmed, the producers are forced to slow down, saving the server from an Out-Of-Memory (OOM) crash. The cost is that if producers are handling network requests, slowing them down means HTTP requests will start timing out for your users.

Watch out for