Teaching predicates and invariants on shared data structures in concurrent programming

Similar documents
CMSC 330: Organization of Programming Languages. Threads Classic Concurrency Problems

Java Monitors. Parallel and Distributed Computing. Department of Computer Science and Engineering (DEI) Instituto Superior Técnico.

CMSC 330: Organization of Programming Languages. The Dining Philosophers Problem

Summary Semaphores. Passing the Baton any await statement. Synchronisation code not linked to the data

Concurrent Programming Benoît Garbinato

The Dining Philosophers Problem CMSC 330: Organization of Programming Languages

CMSC 433 Programming Language Technologies and Paradigms Fall Locks & Conditions

Synchronized Methods of Old Versions of Java

COMP 322: Fundamentals of Parallel Programming

re-exam Concurrent Programming tda383/dit390 Date: Time: 14:00 18:00 Place: Maskinhuset (M)

What's wrong with Semaphores?

Multi-Threaded Programming Design CSCI 201 Principles of Software Development

Producer/Consumer CSCI 201 Principles of Software Development

CMSC 330: Organization of Programming Languages

Threads and Parallelism in Java

COMPSCI 230 Threading Week8. Figure 1 Thread status diagram [

OS06: Monitors in Java

CMSC 132: Object-Oriented Programming II. Threads in Java

CS 162 Operating Systems and Systems Programming Professor: Anthony D. Joseph Spring Lecture 8: Semaphores, Monitors, & Condition Variables

Multiple Inheritance. Computer object can be viewed as

Dr.-Ing. Michael Eichberg

CS 162 Operating Systems and Systems Programming Professor: Anthony D. Joseph Spring 2004

Concurrent Programming using Threads

Condition Synchronization

CSE 153 Design of Operating Systems

Principles of Software Construction: Concurrency, Part 2

Operating Systems CMPSCI 377 Spring Mark Corner University of Massachusetts Amherst

Models of concurrency & synchronization algorithms

MultiThreading 07/01/2013. Session objectives. Introduction. Introduction. Advanced Java Programming Course

CS61B, Spring 2003 Discussion #17 Amir Kamil UC Berkeley 5/12/03

Advanced Java Programming Course. MultiThreading. By Võ Văn Hải Faculty of Information Technologies Industrial University of Ho Chi Minh City

Only one thread can own a specific monitor

CSE332: Data Abstractions Lecture 22: Shared-Memory Concurrency and Mutual Exclusion. Tyler Robison Summer 2010

Component-Based Software Engineering

Chapter 32 Multithreading and Parallel Programming

Software Testing Prof. Meenakshi D Souza Department of Computer Science and Engineering International Institute of Information Technology, Bangalore

Synchronization. CS 475, Spring 2018 Concurrent & Distributed Systems

Programming Language Concepts: Lecture 11

Dr.-Ing. Michael Eichberg

Week 7. Concurrent Programming: Thread Synchronization. CS 180 Sunil Prabhakar Department of Computer Science Purdue University

Programming in Parallel COMP755

Real-Time and Concurrent Programming Lecture 4 (F4): Monitors: synchronized, wait and notify

PROCESS SYNCHRONIZATION

Problems with Concurrency. February 19, 2014

AP COMPUTER SCIENCE JAVA CONCEPTS IV: RESERVED WORDS

Monitors & Condition Synchronization

Performance Throughput Utilization of system resources

Concurrency. Fundamentals of Computer Science

Last Class: Synchronization. Review. Semaphores. Today: Semaphores. MLFQ CPU scheduler. What is test & set?

Lecture 8: September 30

CMSC 433 Programming Language Technologies and Paradigms. Spring 2013

CS11 Java. Fall Lecture 7

Lecture 10 State dependency

Parallel Programming Languages COMP360

Page 1. Goals for Today" Atomic Read-Modify-Write instructions" Examples of Read-Modify-Write "

Monitors; Software Transactional Memory

Computation Abstractions. Processes vs. Threads. So, What Is a Thread? CMSC 433 Programming Language Technologies and Paradigms Spring 2007

Need for synchronization: If threads comprise parts of our software systems, then they must communicate.

CS193k, Stanford Handout #8. Threads 3

JAVA EXAMPLES - SOLVING DEADLOCK

Advanced Concepts of Programming

G52CON: Concepts of Concurrency

JAVA and J2EE UNIT - 4 Multithreaded Programming And Event Handling

Page 1. Goals for Today. Atomic Read-Modify-Write instructions. Examples of Read-Modify-Write

CMSC 330: Organization of Programming Languages

Monitors; Software Transactional Memory

Multithreaded Programming

Multitasking Multitasking allows several activities to occur concurrently on the computer. A distinction is usually made between: Process-based multit

Recap. Contents. Reenterancy of synchronized. Explicit Locks: ReentrantLock. Reenterancy of synchronise (ctd) Advanced Thread programming.

Overview. Processes vs. Threads. Computation Abstractions. CMSC 433, Fall Michael Hicks 1

A Java Execution Simulator

Last Class: CPU Scheduling! Adjusting Priorities in MLFQ!

Condition Variables CS 241. Prof. Brighten Godfrey. March 16, University of Illinois

Concurrency & Parallelism. Threads, Concurrency, and Parallelism. Multicore Processors 11/7/17

Resource management. Real-Time Systems. Resource management. Resource management

Threads. Definitions. Process Creation. Process. Thread Example. Thread. From Volume II

Concurrency. CSCI 136: Fundamentals of Computer Science II Keith Vertanen

Threads, Concurrency, and Parallelism

Page 1. CS162 Operating Systems and Systems Programming Lecture 8. Readers-Writers Language Support for Synchronization

Synchronization SPL/2010 SPL/20 1

THREADS AND CONCURRENCY

Operating Systems ECE344

Today: Synchronization. Recap: Synchronization

Concurrent Objects and Linearizability

sample exam Concurrent Programming tda383/dit390 Sample exam March 2016 Time:?? Place: Johanneberg

Page 1. Goals for Today" Atomic Read-Modify-Write instructions" Examples of Read-Modify-Write "

Synchronization Possibilities and Features in Java

The Contract Pattern. Design by contract

Computation Abstractions. CMSC 330: Organization of Programming Languages. So, What Is a Thread? Processes vs. Threads. A computer.

Threads and Java Memory Model

Threads Chate Patanothai

Info 408 Distributed Applications Programming Exercise sheet nb. 4

Definition: A thread is a single sequential flow of control within a program.

Semaphores and Monitors: High-level Synchronization Constructs

G Programming Languages Spring 2010 Lecture 13. Robert Grimm, New York University

So far, we've seen situations in which locking can improve reliability of access to critical sections.

More Synchronization; Concurrency in Java. CS 475, Spring 2018 Concurrent & Distributed Systems

Lecture 9: Introduction to Monitors

CPS 310 second midterm exam, 11/14/2014

Java s Implementation of Concurrency, and how to use it in our applications.

Transcription:

Teaching predicates and invariants on shared data structures in concurrent programming Stein Gjessing Department of informatics, University of Oslo steing@ifi.uio.no Arne Maus Department of informatics, University of Oslo arnem@ifi.uio.no Abstract In concurrent programing, threads may communicate via data structures that can be embedded in shared objects or monitors. In this paper we outline the basis for a short module that can be used to teach better programming of such monitors by emphasizing logical statements, called predicates, on the data structures inside these shared objects. We base the module on the general concepts of object oriented programming, where the (usually) private data structure is manipulated by public methods, and where an invariant predicate holds before an after the execution of each method. An important challenge when writing a monitor is to program correct synchronization, and the main contribution of this paper is to show how to teach the students to use predicates to reason about, and make code that performs correct waiting and signaling inside monitors. Categories and Subject Descriptors D.3.3 [Programming Techniques]: Concurrent Programming, Object Oriented Programming. D.2.4 [Software Engineering]: Software/Program Verification General Terms: Algorithms, Design, Documentation, Languages, Theory, Verification, Keywords Threads, Monitors, Predicates, Invariants, Synchronization, Didactics 1. Introduction In a second semester object oriented programming course we teach students concurrent programming based on classes and objects, concepts that they already know. They learn to program threads, and to let threads communicate and collaborate using shared objects or monitors [1] (The term monitor is a loaded word, but in a parallel programing context the term monitor was coined by C.A.R. Hoare in 1974 as a shared object.). Students learn to reason about the state of the data structure of the program they are writing, using logical statements or predicates. When they write classes, they have to reason about the internal (usually private) data structures in the objects, and how the public methods change the state of these data structures. The consistency of the data structures inside an object can be expressed by an invariant predicate, and all public methods have to preserve this invariant. It may temporarily be broken during the execution of a method, but it will be restored before the method terminates. We emphasize, however, that such a proof (that an invariant is maintained by all methods) is very short of a full proof of the class, e.g. it does not express that each method performs the task it is intended to. A full verification of a class is usually a too lengthy, complicated and also error prone activity to be taught in a second semester course. The use of invariants is a kind of formalism light suitable for the kind of problems treated here, and analogous to tabular specification of methods [3]. When threads and monitors are introduced, we thus build on this knowledge about invariants in classes. 2. A restaurant table for a sequential program The archetypical example of a monitor is a shared buffer where producers can put data and consumers can get data. To make the situation more memorable for the students, instead of a generic buffer we use an example in the form of a limited sized counter or a serving table in a restaurant, where dishes (single plates) are placed (under heating lights) after being produced by chefs, waiting for the waiters to be served (fig. 1). In order to make the programs in this paper as small as possible, we do not represent the individual dishes. It would be very easy to add this feature to the programs shown. With this simplification we only need to have a data structure with one variable, called nosontable, in the object. Based on this specification of the data structure, the students easily write down a simple invariant: 0 nosontable TABLE_SIZE put ge t Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. SIGPLAN 05 June 12 15, 2005, Location, State, Country. Copyright 2004 ACM 1-59593-XXX-X/0X/000X $5.00. Table Chefs Waiters Figur 1. Three producer threads (chefs) and three consumer threads (waiters) performing respectively put and get operations on a shared table implemented as a monitor.

where TABLE_SIZE is the maximum number of plates that can simultaneously be placed on the heating table, i.e. the buffer size. However, we first ask the students to write a class for our restaurant heating table that is to be used in a sequential program. Then they produce something like the program shown in fig. 2. Since the individual dishes are not represented, both methods returns a boolean value telling if the operation was successful or not. class HeatingTable { final int TABLE_SIZE = 10; int nosontable = 0, // Invariant: 0 nosontable TABLE_SIZE boolean putplate() { if (nosontable == TABLE_SIZE) { // 0 nosontable < TABLE_SIZE nosontable++; // 0 < nosontable TABLE_SIZE boolean getplate() { if (nosontable == 0) // 0 < nosontable TABLE_SIZE nosontable--; // 0 nosontable < TABLE_SIZE Figure 2. Program showing a heating table that can store 10 plates. To be used in a sequential program. The invariant obviously holds initially (nosontable = 0), and we have to make sure that all methods preserve this invariant. In the putplate method the invariant can be broken by the execution of the operation nosontable++. If we can establish that nosontable < TABLE_SIZE before this operation, we know it can be executed without breaking the invariant. Hence we test whether nosontable already has reached TABLE_SIZE. If it has, we exit the method without doing anything, and we return false in order to show this to the calling program. In fig. 2 we see the two comments (the predicates) that describes the state of the monitor respectively before and after this increment operation in the putplate method. The argument why the method getplate in fig. 2 also preserves the invariant, is symmetric. We have not written down the full invariant in front of (nor behind) all the methods. 3. A first concurrent solution In a concurrent program, where more threads operate on shared objects, if the execution of a method seems to break the object s invariant, the method does not have to return immediately with mission unaccomplished. Instead the thread s execution of the method can wait a while, and the thread can hope that another thread will call a method in the object, and that the execution of this method will change the state of the data structure inside the object in such a way that the waiting thread now can finalized the execution of its method without breaking the invariant. Hence we teach the students that they can program the operations so that they may wait for the state to be changed (by one of the other threads), and when the wait is finished, the state is hopefully (ideally always) so that the intended method can be executed without breaking the invariant. Before a wait statement the students have to write down in the program a predicate that show that the rest of the method can not be executed without breaking the invariant. When the wait is completed, they have to write a predicate that shows that the method may now be completed while preserving the invariant. We then also teach them that methods must notify other waiting threads, when the state of the data structure has changed so that these other threads may not have to wait any more. The predicate that indicates why a notify operation should be executed must be inserted before the notify. When teaching this, we first just ask the students to turn the program in fig. 2 into a monitor. A chef that is making a dish when the table is full, then have to wait inside the putplate method, and a waiter that wants to serve a dish has to wait inside the getplate method if the table is empty. In this paper, we do not show this first simple monitor. It is then easy to write a program with chef threads and waiter threads that run a restaurant indefinitely. If, however, that task becomes to produce and serve a given number of dishes, and also terminate all threads in an orderly fashion, the monitor becomes a little more complex. In addition to the TABLE_SIZE, we then have the number of plates to be produced, called NOS_TO_BE_MADE. A variable in the object, called nosproduced, then counts how many plates have been made and put on the table so far. Hence the invariant now consists of two parts that must both hold: 0 nosontable TABLE_SIZE nosproduced NOS_TO_BE_MADE In this problem we hence have four 'balls in the air' - the three inequalities stated in the two invariants and the demand on normal terminations on all but the main thread when the job is done, all plates made and served. We also specify that when NOS_TO_BE_MADE dishes have been produced and placed on the table, the putplate method shall return false, so that the chefs may terminate. Likewise the method getplate shall return false if there is nothing more to serve. Otherwise the methods return true. As we have already pointed out, in order to simplify the program, the dishes are not represented in the programs in this article. An obvious way to do this is to let putplate have a dish or a plate as a parameter, and let getplate return a plate instead of a boolean. Instead of a false return one would then get a null return when there are no more plates to serve. When students run programs with threads and monitors, it is very important to visualize the execution sequence inside the monitor. Hence we tell them to write output that shows how threads are sequenced inside the monitor, and when they wait and when they wake up. In order to do this it is advantageous to bring along a thread identification when a monitor method is called. In fig. 3, the two methods putplate and getplate both have a parameter that is a reference to the calling thread, and statements are given that will produce informative output when the program is run. The students can also play with different numbers of chefs, waiters and dishes to be produced, and they can change the time the chefs and the waiters are sleeping before they access the monitor again. The main program file that includes the chef threads and the waiter threads are given in the Appendix.

class HeatingTable{ // A monitor int nosontable = 0; int nosproduced = 0; // Invariants holds initially final int TABLE_SIZE; final int NOS_TO_BE_MADE; // INVARIANTS: // 1) 0 <= nosontable <= TABLE_SIZE // 2) nosproduced <= NOS_TO_BE_MADE HeatingTable(int maxon, int max) { TABLE_SIZE = maxon; NOS_TO_BE_MADE = max; // Invariants holds initially as long as // parameters are none-negative public synchronized boolean putplate(chef c) { while (nosontable == TABLE_SIZE && nosproduced < NOS_TO_BE_MADE) { // The while test holds here meaning // that a chef should but can not // make a dish, because the table is full System.out.println("Chef "+c.ind+ " WAITING to make a dish"); wait(); catch (InterruptedException e) { // Insert code to handle interrupt // one or both of the loop conditions are now false if (nosproduced < NOS_TO_BE_MADE) { // Hence OK to increase nosontable nosontable++; // nosproduced < NOS_TO_BE MADE // Hence OK to increase nosproduced: nosproduced++; c.dishnum = nosproduced; System.out.println("Chef "+ c.ind+ makes plate num:" + c.dishnum ); // nosontable > 0 // Wake up a waiting waiter, // or all if nosproduced == NOS_TO_BE_MADE notifyall(); if (nosproduced == NOS_TO_BE_MADE) { else{ else { // nosproduced == NOS_TO_BE_MADE // end putplate public synchronized boolean getplate(waiter w) { while (nosontable == 0 && nosproduced < NOS_TO_BE_MADE ) { // The while test holds here // meaning that the table is empty and // there is more to serve System.out.println("Waiter " + w.ind + " WAITING to serve a dish" ); wait(); catch (InterruptedException e) { // Insert code to handle interrupt //one or both of the loop conditions are false if (nosontable > 0) { // 0 < nosontable <= TABLE_SIZE // Hence OK to decrease nosontable: nosontable--; System.out.println("Waiter "+w.ind+"serves + plate num:" + (nosproduced-nosontable) ); // Must wake up a sleeping chef: notifyall(); if (nosproduced == NOS_TO_BE_MADE && nosontable == 0) { else{ else { // nosontable == 0 && // nosproduced == NOS_TO_BE_MADE // end getplate // end class HeatingTable Figure 3. A bounded buffer table with a given number of dishes to be produced. This example uses Java built in waiting and signaling. Notice that we have decorated the program in fig. 3 with predicates telling the state of the data structure at this point in the program. These predicates are the essence of our message to the students, and what we want to teach them to use in order for them to be able to write programs that they can convince themselves and others are correct. Initially we ask the students to write down predicates as they have learned for objects with private data structures and methods that manipulate this structure in sequential programming. I.e. they find and write the invariant down at the start of all methods, and then they must show that it holds for all possible terminations of the methods. We also want them to write down the usual facts that the while test holds immediately inside the while block, and that the negation holds immediately after the while statement (and similar for if-tests). The program in fig. 3 could have been decorated with even more of these predicates. Then we concentrate on the particular aspects of predicates inside monitor methods. The most important predicates in fig. 3 are the ones in front of the wait()-statements. These predicates state the fact that the threads has to wait because the state of the monitor is such that it is currently impossible to complete the execution of the methods without violating the invariant. When it is safe to complete the execution of the methods, the predicates before the increment and decrement operations of the two object variables ensure this. These predicates tell that it is safe to execute these operations without violating the invariant. The predicates after the while loops, and the if-tests ensure these predicates. In native Java there is, for each shared object, one queue of threads waiting, after having executed any wait()-operation in any method. It is possible to reason about which (textually) waitoperations the threads in this queue have performed. However, such predicates that include reasoning about the queue of waiting threads, is complex, and only much later in the curriculum we tell the students about the types of invariants that are needed in this case.

Because there is only one queue of waiting threads when using native Java synchronization, we can not know whether a notify()- operation will restart a thread waiting in the putplate method or in the getplate method. A chef that have just put a plate on the table must be sure that it is waking up a waiter. If it performs a simple notify() opeation, it may wake up another chef instead. Hence in order to be sure that the putplate method notifies a waiter waiting in the getplate method it has to use the notifyall() operation. This is of course also the case in the getplate method where a waiter notifies a chef waiting in the putplate method by a notifyall() operation. Hence, by using notifyall(), and reasoning about the invariant and the other predicates included in the program of fig. 3, it is possible for the students to convince themselves and others that the program is correct. Obviously it is not an ideal solution to wake up all waiting threads when only one plate has been put on (or taken from) the table. What we really want is one waiting queue for each different state in which the execution has to wait before a certain operation can be executed without breaking the invariant. A high level view of what we want is shown in fig. 4. Figure 4. A monitor with one queue for each condition that a thread can wait on. In this case there is one queue waiting for the table to be not full, and one queue waiting for the table to be not empty. 4. A second parallel solution using Conditions Fortunately Doug Lea has made an addition to the Java library [2] that makes it possible to use two (or more) queues when methods are waiting for different predicates or conditions to hold. This addition, java.concurrent, offers the ReentrantLock class. Then we can specify different predicates (or conditions) that threads have to wait for to hold. The ReentrantLock class provides a fabric for Condition variables. Hence, for each different predicate the program need to wait for to hold, we define a Condition variable, and all threads waiting for the same predicate to hold may now be placed in the same queue (by an await() operation), and this is exactly what we want: When one thread has changed the state so that another thread may now seize waiting, we can signal() this other thread to be restarted. import java.util.concurrent.locks.*; class HeatingTable{ // Monitor int nosontable = 0; // Invariant holds initielt int = 0; // Invariant holds initielt final int TABLE_SIZE ; final int NOS_TO_BE_MADE; // INVARIANTS // 1) 0 <= nosontable <= TABLE_SIZE // 2) nosproduced <= NOS_TO_BE_MADE HeatingTable(int maxon, int max) { TABLE_SIZE = maxon; NOS_TO_BE_MADE = max; // Invariants holds initially as long as // the parameters are none-negative final Lock lock = new ReentrantLock(); final Condition notfull = lock.newcondition(); final Condition notempty = lock.newcondition(); public boolean putplate(chef c) throws InterruptedException { lock.lock(); while (nosontable == TABLE_SIZE && nosproduced < NOS_TO_BE_MADE) { // The while test holds here System.out.println("Chef "+c.ind+ " WAITING to make a dish"); notfull.await(); // one or both of the loop conditions are now false if (nosproduced < NOS_TO_BE_MADE) { nosproduced++; c.dishnum = nosproduced; nosontable++; System.out.println("Chef number "+ c.ind+ " makes plate num:" + c.dishnum ); // nosontable > 0 notempty.signal(); if (nosproduced == NOS_TO_BE_MADE) { // Last plate is produced notempty.signalall(); // tell Chefs to stop // waiting and terminate notfull.signalall(); // Not more to wait for, // Waiters serve last plate(s) else { finally { lock.unlock(); // end put public boolean getplate(waiter w) throws InterruptedException { lock.lock(); while (nosontable == 0 && nosproduced < NOS_TO_BE_MADE ) { // The while test holds here System.out.println("Waiter " + w.ind + " WAITING to serve a dish" ); notempty.await();

// one or both of the loop conditions are now false if (nosontable > 0) { nosontable - -; System.out.println("Waiter number "+ w.ind + " serves plate num:" + (nosproduced-nosontable)); notfull.signal(); else { // nosontable == 0 && // nosproduced == NOS_TO_BE_MADE finally { lock.unlock(); // end get // end HeatingTable Figure 5. The bounded buffer table with a number of dishes to be produced. This example use the ReentrantLock class in the Java Concurrent library. Unfortunately the library does not explicitly state that the signaled thread immediately take over the monitor s lock when the current thread releases the lock. In this way we do not know exactly what the state of the monitor data structure is when a waiting thread is restarted. Below (in the last paragraph of this section) we describe why the consequences of this fault is not very grave. The new monitor is given in fig. 5. Unfortunately the built in Java synchronization lock (using the keyword synchronized) can not be used, so we explicitly have to lock and unlock the monitor. The logic of the program in fig. 5 is exactly the same as the previous one in fig. 3. The predicates in the code are also the same. The difference is that the language constructs (in the java.concurrent library) now much better fit the tools we are using in order to convince ourselves and others that the program is correct. Note the small asymmetry in the solution. When a chef has produced the last plate, it has to signal all waiting chefs and all waiting waiters: There are noting more to do. Otherwise a chef and a waiter now only signals and restarts one other thread, respectively a waiter and a chef. Notice that we have kept the while loop around the awaitoperation. In this program a waiter or a chef may also be woken up because there is nothing more to do (nosproduced == NOS_TO_BE_MADE), hence the condition must be re-checked when they wake up. Also in general, even when using Condition variables, and as mentioned above, the documentation of the library does not explicitly state that a signaled thread will acquire the lock immediately after the signaling thread. Hence, in any case, the program still have to re-check the condition after having been woken up. Fortunately, that is not so bad, since this in any case makes for a more robust program. The reason that a signaled thread is not guaranteed to acquire the lock immediately after the signaling thread is that a thread may in general signal more then one other thread. 5. Conclusion We have outlined the content of a short module that can be used to teach students concurrent programming by Java threads that communicate via shared data structured encapsulated in monitors, as first advocated by C.A.R. Hoare [1]. One sequential and two slightly different parallel solutions to a bounded buffer with a maximum number to be produced are used as examples. The main contribution of this paper is to show how reasoning about the state of an object is important, especially in concurrent programing. Our students have to reason about (private) data structures in objects from when they first learn to program such objects. They learn that in any object with a private data structure and public methods, invariants must be formulated and documented. When they learn about threads and shared data structures, we extend this knowledge to the use of invariants and predicates on the monitor data structure. This helps the students to formulate predicates and reason about when to wait and when to signal. The two examples of a monitor in this article solve the same problem, and have the same invariant, but one example performs the task much more elegantly than the other. Based on the predicates that hold when threads wait and threads signal, we show that the built in wait() and notify() operations embedded in the Java programming language are not good enough tools. We also show that the library java.concurrent written by Dough Lea [2], comes much closer to include tools that fits the way we believe it is natural and correct to express the problem using invariants and predicates. In concurrent programming correct reasoning about the state of (shared) objects are even more important than in sequential programs, because incorrect concurrent programs are even harder to debug than sequential programs, and an erroneous concurrent program may compute correctly for a long time before it fails. Appendix class Restaurant { HeatingTable table; Restaurant(String[] args) { table = new HeatingTable(3,Integer.parseInt(args[0])); int numchefs = Integer.parseInt(args[1]); int numwaiters = Integer.parseInt(args[2]); while (numwaiters-- > 0) new Waiter(table,numWaiters+1 ).start(); while (numchefs-- > 0) new Chef(table,numChefs+1).start(); // end constructor public static void main(string[] args) { Restaurant rest; if (args.length!= 3) { System.out.println("use: >java Restaurant + <maxdishes> <#Chefs> <#Waiters>"); else{ rest = new Restaurant(args); // end main // end Restaurant class Chef extends Thread { HeatingTable sharedtable; int ind; int dishnum;

Chef(HeatingTable shared, int ind) { sharedtable = shared; this.ind = ind; public void run() { while (sharedtable.putplate(this)) { sleep((long) (1000 * Math.random())); catch (InterruptedException e) { // Chef has finished System.out.println("Chef number finished: " + ind); // end Chef References 1. C.A.R. Hoare: Monitors, a parallel programming consept, Comm ACM, March 1974. 2. Doug Lea: Concurrent Programming in Java: Design Principles and Patterns (Java Series), Addison Westley 1996. 3. Xin Feng, David Lorge Parnas, T.H. Tse, Tony O'Callaghan, "A Comparison of Tabular Expression-Based Testing Strategies," IEEE Transactions on Software Engineering, vol. 37, no. 5, pp. 616-634, Sept.-Oct. 2011, doi:10.1109/tse.2011.78. class Waiter extends Thread { HeatingTable sharedtable; int ind; Waiter(HeatingTable shared, int ind) { sharedtable = shared; this.ind = ind; public void run() { while ((sharedtable.getplate(this))){ // serve the plate sleep((long) (1000 * Math.random())); catch (InterruptedException e) { System.out.println("Waiter number finished: " + ind); // This Waiteren has finished // end Waiter Figure 6. The Restaurant class, the Chef class and the Waiter class that all can use the HeatingTable monitor.