Implementing a Restaurant Ordering System with Semaphores

Implementing a Restaurant Ordering System with Semaphores

Welcome back! In our previous lesson, we examined how to simulate a car manufacturing line with multiple synchronized stages using Phaser and ExecutorService. That exercise helped illustrate the power of managing thread coordination in complex tasks. Today, we're shifting focus to another practical application of Java concurrency: implementing a restaurant ordering system using semaphores. This lesson aims to build on your concurrency knowledge, emphasizing resource management and thread synchronization.

What You'll Learn

In this lesson, you will explore:

  • The concept of resource management through semaphores.
  • How to handle concurrency when multiple threads access limited resources concurrently.
  • Implementing a practical simulation of a real-life scenario where semaphores manage resource constraints effectively.

Recap: Using Semaphores and Their Role in Resource Management

You’ve previously learned about semaphores as a concurrency mechanism that helps control access to limited resources. A semaphore allows only a certain number of threads to access a resource at any given time. When a thread completes its task, it releases its hold on the semaphore, allowing another thread to proceed. This is particularly useful when simulating real-world systems where resources (like chefs in a restaurant) are limited.

In this lesson, we will use semaphores to simulate a restaurant with multiple waiters taking orders and a limited number of chefs preparing the orders. The semaphore will ensure that only a specified number of orders are being prepared simultaneously, mimicking the constraint of having a limited number of chefs. The ExecutorService will manage the waiters, who submit orders to be processed.

Setting Up the Restaurant System

Let's first set up the basic structure of our system. We begin by initializing the semaphore and the executor service:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class RestaurantOrderingSystem {

    private final Semaphore chefSemaphore;
    private final ExecutorService executor;

    public RestaurantOrderingSystem(int numberOfChefs, int numberOfWaiters) {
        this.chefSemaphore = new Semaphore(numberOfChefs);
        this.executor = Executors.newFixedThreadPool(numberOfWaiters);
    }
}

In the above code, the Semaphore is initialized with the number of available chefs, controlling how many orders can be processed simultaneously. ExecutorService manages multiple threads representing waiters.

Here’s what the key methods in the above snippet do:

  • newFixedThreadPool(): This method creates a fixed pool of threads where the number of waiters taking orders is controlled.
  • Semaphore: Manages the number of chefs available to process orders at a time, ensuring that only the allowed number of orders are prepared simultaneously.
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal