Collaboration between Threads (ii) producers and consumers

Source: Internet
Author: User

Consider such a restaurant, it has a chef (chef) and a waiter (waiter). The waiter must wait for the chef to prepare the dishes. When the chef is ready, he notifies the waiter, then the waiter will serve, then return to wait. This is an example of a task collaboration: The chef represents the producer, and the waiter represents the consumer. Both tasks must be shook when the dish is produced and consumed, and the system must be closed in an orderly manner. Here is the code for modeling this narrative:

import java.util.concurrent.executorservice;import java.util.concurrent.executors;import  java.util.concurrent.timeunit;class meal {    private final int  Ordernum;    public meal (Int ordernum)  {         this.ordernum = ordernum;    }    @ Override    public string tostring ()  {         return  "meal "  + orderNum;    }}class Waiter  implements runnable {    private restaurant r;     Public waiter (Restaurant r)  {        this.r =  r;    }     @Override     public void  run ()  {        try {             while (! Thread.interrupted ())  {                 synchronized  (This)  {                     while (r.meal == null)  {                          wait ()//Waiting for chef to cook                      }                 }                 system.out.println ("waiter got "  + r.meal);                 synchronized  (R.chef)  {                     r.meal  = null;//Serving                      r.chef.notifyall ();//Inform the chef to continue cooking                  }             }        } catch  (interruptedexception  e)  {            system.out.println (" Waiter task is over. ");         }    }}class chef implements  runnable {    private restaurant r;    privThe number of dishes ate int count = 0;//cooks     public chef (Restaurant r)  {        this.r = r;    }      @Override     public void run ()  {         try {             while (! Thread.interrupted ())  {                 synchronized  (This)  {                     while (r.meal != null)  {                          wait ();//waiting for waiter to serve                      }                 }                 if  (++count > 10)  {                     system.out.println ("Meal is enough,  stop. ");                      r.exec.shutdownnow ();                 }                 system.out.print ("order up! ");                 synchronized  (R.waiter)  {         &nBsp;           r.meal = new meal (count) ;//Cooking                      r.waiter.notifyall ();//Notify Waiter of serving                  }                 timeunit.milliseconds.sleep (;        )     }        } catch  ( Interruptedexception e)  {             System.out.println ("Chef task is over.");         }    }}public class restaurant  {    meal meal;    executorservice exec&nbsP;= executors.newcachedthreadpool ();     //cooks and waiters serve the same hotel      Waiter waiter = new waiter (this);     chef chef = new  chef (this);     public restaurant ()  {         exec.execute (waiter);         exec.execute (chef);     }    public static void main (String[] args)  {         new restaurant ();     }}

Execution Result:

Order up! Waiter got Meal 1Order up! Waiter got Meal 2Order up! Waiter got Meal 3Order up! Waiter got Meal 4Order up! Waiter got Meal 5Order up! Waiter got Meal 6Order up! Waiter got Meal 7Order up! Waiter got Meal 8Order up! Waiter got Meal 9Order up! Waiter got Meal 10Meal is enough, stop. Order up! Waiter task is over. Chef task is over.

Restaurant is the focus of waiter and chef, and they all have to know which restaurant to work for, because they have to deal with the restaurant's window and place or take a dish r.meal. In Run (), waiter enters the wait () mode, stopping its task until it wakes up by chef's Notifyall (). Since this is a very simple program, we know that only one task will be waiting on the waiter lock: the waiter task itself. For this reason, it is theoretically possible to call notify () instead of Notifyall (). However, in more complex situations, there may be multiple tasks waiting on a particular object lock, so you don't know which task should be awakened. So calling Notifyall () is a little more secure so that you can wake up all the tasks waiting for the lock, and each task must determine whether the notification is relevant to itself.

Once chef sends meal and notifies waiter, the chef waits, knowing that waiter collects the order and notifies chef that chef can make the next menu.

Note that wait () is wrapped in a while () sentence, which is constantly testing the things that are waiting. At first glance a bit strange-if waiting for an order, a single You are awakened, this order must be available, right? As noted earlier, in more complex concurrent applications, some other task may suddenly get in and take orders when waiter is awakened. So the only safe way to do this is to use the following wait () idiom:

while (Conditionisnotmet) {wait ();}

This guarantees that the condition will be met before you exit the wait loop, and that if you receive a notification about something that has nothing to do with the condition, or if the condition changes before you completely exit the wait loop, you can ensure that you return to the waiting state.

Note that the call to Notifyall () must first capture the lock on waiter, and the call to wait () in Waiter.run () will automatically release this, so this is possible. Because calling Notifyall () must have this lock, this ensures that two of the tasks that attempt to invoke Notifyall () on the same object do not conflict with each other.

By putting the entire run () method body into a try statement block, the two run () methods are designed to be closed in an orderly manner. The catch clause ends next to the parentheses of the run () method, so if the task receives interruptedexception, it will end immediately after the exception is caught.

Note that in chef, after calling Shutdownnow (), you should return directly from run (), and this is usually what you should do. However, there are some more interesting things to do in this way. Remember that Shutdownnow () will send interrupt () to all executorservice-initiated tasks, but in chef, the task does not end immediately at the time that the interrupt () is obtained. This interrupt can only throw interruptedexception when the task attempts to enter a (interruptible) blocking operation. So you'll first see "Order up!", and then chef tries to call the sleep () method, throwing a interruptedexception. If you remove the call to sleep (), the task will go back to the top of the run () loop and exit because of the thread.interrupted () test without throwing an exception.

In both of these examples, there is only one single place for a task to hold the object, so that another task can later use the object. However, in a typical producer-consumer implementation, a FIFO queue should be used to store the objects that are produced and consumed.


Collaboration between Threads (ii) producers and consumers

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.