Easy recipe to Java Multi-Threading

Multi-threading in java is considered as a complex piece to understand. In this article, I am trying to explain multi-threading implementation with some live examples. Intention behind this is to first sense the requirement of any operation/method and then the usages of it.

However, given analogy may not exactly fit the use case but still I hope we can bear with it for sake of understanding. So please bear with me.

I would like to take instance of very usual work flow over any usual food joint where tasks are divided among various group of people like shef will cook or prepare the food item, some people got responsibility to package the order and some manage the counter who interact with customer and forward the order for fulfilment. Another actor I can foresee is Customer. I would like to name this food joint as Bollywood food point and here is the behavior of store that I would like to simulate

-          In my store, there are 3 shefs (Sharukh, Salman and Amir), 1 packager (Vivek) and two counter managers (Alia, Shraddha and Pari). When a store opens, all of them are available for service and waiting for order to get placed.
-          Whenever a customer visits the store the activities followed are-
o   Customer aligned to one of the counter person
o   Counter person greet customer and ask for order
o   An order number is saved against this transaction
o   Customer place the order
o   Counter person calculate the bill and notify customer
o   Customer pays bill and wait for order
o   Counter person sends the order for fulfillment
o   Shef prepare the order. If order has multiple items, multiple shef prepare it
o   Once order is done, it is packaged by packager
o   System monitor shows the fulfilled order number
o   Customer receives the order.


In above example, the entire processing is done in single container i.e. your joint. This can be simulated as a process. Like a store, process is an executable which performs a set of action and has all the required dedicated resources like memory etc.
Each action in above example which can process independently can be assumed as Thread. A thread is sequence of action under a process which can be independently executed. Its periphery remains within the process and can share data among themselves as well as with process.


To start with, let’s simulate this for one customer (Sachin) to remain it simple. Sachin requests for one Veg Burger and some fries. With coming example, we would increase the customer and mock the actual multi-threading usages.
Here we go:
Let’s create our service persons first. Assume each job actor as a thread. So we have to create three threads – shef, packager and counter person. Also, we would need one thread to act as customer as well.


There are two ways to create a thread, one is to implements Runnable interface; other is to extend the thread class. The preference criteria between both approaches can be driven by Inheritance choice. If a class need to extend some other class, it should go by interface way, otherwise extending class will do.
In both ways, one abstract method void run(), need to be override. The processing which we would like to perform by the thread should go in run definition.


Shef, packager and counterperson can be categorized as service staff and expected to have some common properties like name, current order and details. Hence I would like to create an abstract parent class for it.
ServiceStaff.java
public abstract class ServiceStaff {

      /**
       * @param currentOrderId
       * @param name
       */
      public ServiceStaff(int currentOrderId, String name,Order order) {
            this.currentOrderId = currentOrderId;
            this.name = name;
            this.order=order;
      }

      private Order order;

      public Order getOrder() {
            return order;
      }

      public void setOrder(Order order) {
            this.order = order;
      }

      private int currentOrderId;

      private String name;

     
      public int getCurrentOrderId() {
            return currentOrderId;
      }

      public void setCurrentOrderId(int currentOrderId) {
            this.currentOrderId = currentOrderId;
      }

      public String getName() {
            return name;
      }

      public void setName(String name) {
            this.name = name;
      }
}
Shef.java
public class Shef extends ServiceStaff implements Runnable {

      public Shef(int currentOrderId, String name, Order order) {
            super(currentOrderId, name, order);
      }

      @Override
      public void run() {
            System.out.println(getName() + ":Preparing order: "
                        + getCurrentOrderId());
            try {
            // Mocking the time consumed by the Shef in preparing the item
                  Thread.sleep(5000);
            } catch (InterruptedException e) {
                  e.printStackTrace();
            }
            System.out.println(getName() + ":Prepared order" + getCurrentOrderId());
      }

}



In above example you must have observed two different constructs - Thread.sleep() and InterruptedException.

Sleep is a static method of class thread, which causes the current thread to go in idle state for the given time as parameter. Thread is moved from running to idle till the stated time and restored in runnable state after it.

InterruptedException is the exception thrown by some methods of thread which drives state of a thread. When this exception occurs, it meant that some other thread wants to interrupt the called thread from its current state. More details will be provided in coming sections of this article



Packager.java
public class Packager extends ServiceStaff implements Runnable {

      public Packager(int currentOrderId, String name, Order order) {
            super(currentOrderId, name, order);
      }

      @Override
      public void run() {
            packageOrder();

      }

      private void packageOrder() {
            System.out.println(getName()+":Packing order #" + getCurrentOrderId());
            try {
                  // mocking the packaging time
                  Thread.sleep(1000);
            } catch (InterruptedException e) {
                  e.printStackTrace();
            }
            System.out.println(getName() + ":Packed order #" + getCurrentOrderId());

      }

}

CounterPerson.java
public class CounterPerson extends ServiceStaff implements Runnable {

      public CounterPerson(int currentOrderId, String name, Order order) {
            super(currentOrderId, name, order);
      }

/**
* This class demonstrate how to create a thread by implementing
* runnable.
*
* Functionality of this class is mocking the behavior of CounterPerson * at entry in snacks joint */

      @Override
      public void run() {
            welcomeCustomer();
            processOrder();
            calculateBill();
            thankCustomer();
      }

      private void thankCustomer() {
            System.out.println(getName() + ":thankCustomer ...");

      }

      private void calculateBill() {
            System.out.println(getName() + ":calculateBill ...");
           
      }

      private void processOrder() {
            System.out.println(getName() + ":processing Order ...");
           
            System.out.println(getName() + ":Processed ...");

      }

      private void welcomeCustomer() {

            Calendar cal = Calendar.getInstance();
            if (cal.get(Calendar.AM_PM) == Calendar.PM) {
                  System.out.println(getName() + ":Hello ...");
            } else {
                  System.out.println(getName() + ":Good Morning ...");
            }
            System.out.println(getName() + ":What would you like to have");
           

      }
}

Note: These classes are just drafts and we will keep on modifying them to mock behavior accordingly.



Customer class doesn’t have any hierarchy hence we can implement it by extending thread class.



Customer.java
public class Customer extends Thread {

      private Order order;
      private String customerName;

      public String getCustomerName() {
            return customerName;
      }

      public void setCustomerName(String customerName) {
            this.customerName = customerName;
      }

      /**
       * @param order
       */
      public Customer(String customerName, Order order) {
            this.customerName = customerName;
            this.order = order;
      }

      public Order getOrder() {
            return order;
      }

      public void setOrder(Order order) {
            this.order = order;
      }

      /**
       * This class demonstrates how to create a thread by extending thread class.
       *
       * Functionality of this class is mocking the behavior of portier at exit in
       * snacks Chain joint
       */


      @Override
      public void run() {
            greetBack();
            placeOrder();
           
            payBill();

      }

      private void placeOrder() {

            System.out.println(customerName + ":" + " placing order ...");
           
            }

      }

      private void payBill() {
            System.out.println(customerName + ":" + "paying bill ...");
           
            }

      }


      private void greetBack() {

            Calendar cal = Calendar.getInstance();
            if (cal.get(Calendar.AM_PM) == Calendar.PM) {
                  System.out.println(customerName + ":" + "Hello ...");
            } else {
                  System.out.println(customerName + ":" + "Good Morning ...");
            }
            System.out.println(customerName + ":" + "I would like to have");

      }
}

We would need some more related entity classes like order, menu, order item etc.
Item.java
public class Item {

      /**This class represent single item in the menu or order
       * @param itemName
       * @param cost
       */
      public Item(String itemName, int cost) {
            this.itemName = itemName;
            this.cost = cost;
      }

      private String itemName;
      private int cost;


      public String getItemName() {
            return itemName;
      }

      public void setItemName(String itemName) {
            this.itemName = itemName;
      }

      public int getCost() {
            return cost;
      }

      public void setCost(int cost) {
            this.cost = cost;
      }

}
Menu.java
public enum Menu {
      VEGBURGER(new Item("VegBurger", 60)),
PANEERWRAP(new Item("Paneer Wrap",200)),
FRENCHFRIES(new Item("French Fries", 80)),
COKE(new Item("Coke", 30)),
MACPUFF(new Item("Mac Puff", 20));

      final Item item;

      public Item getItem() {
            return item;
      }
      Menu(Item item){
            this.item = item;
      }
}

Order.java


public class Order {
      private int orderId;
      private int totalCost;
      private List<Item> menuItems = new ArrayList<Item>();
      private String status = "New";

      public String getStatus() {
            return status;
      }

      public void setStatus(String status) {
            this.status = status;
      }

      public int getOrderId() {
            return orderId;
      }

      public void setOrderId(int orderId) {
            this.orderId = orderId;
      }

      public int getTotalCost() {
            return totalCost;
      }

      public void setTotalCost(int totalCost) {
            this.totalCost = totalCost;
      }

      public List<Item> getMenuItems() {
            return menuItems;
      }

      public void setMenuItems(List<Item> menuItems) {
            this.menuItems = menuItems;
      }

}


No comments:

Post a Comment