CSS 430 for 2016 Summer
Program 3: Synchronization
Due Date: See the syllabus
This document is final (please begin work on it)
(note
that the grading guide has
been posted - it's at the end)
This assignment has two overall sections. In the first section you will examine code provided in your textbook and explain how a problem might (or might not) occur when the provided code is modified. In the second section you will write multithread code that needs to be synchronized for correct execution.
For this assignment you must explain all your answers in your own words. Plagiarism will be harshly punished
The questions that you need to answer are listed in the following sections (i.e., section 2, section 3, etc).
For each question clearly describe the problem that can occur in each situation (or else clear explain there will be no problems)
Please start by clearly stating whether a problem could occur or not (just so it's clear to the instructor what you're arguing in favor of :) )
If the change will never cause any problems then explain why the change to the code is benign. Depending on the details of the code the most direct way to explain why the changes are beneign is to enumerate all possible scenarios that the program might encounter and then show why each one is ok (This blue highlighted material was added on August 1st - if it's useful please use it BUT if you've already got good answers then don't worry about it).
If a problem can occur then clearly explain how the situation could
happen. Do this by downloading a 'Process
Problem Chart' (one for each question), and filling it in.
Start by explaining what
the processes were doing prior to the problem.
Next, provide a clear,
concise explanation of the problem itself.
Lastly, fill in one table per
process detailing exactly what each process was doing when the problem
occurred.
Notes on filling in the chart:
Some general notes about your answers:
mutex
variable was left out." focus your
answer on the program would be behave differently when the mutex
variable was completely removed from the program. An answer like
"When you remove line 25 (private Semaphore mutex;
) the
program won't compile because the variable mutex is undefined" will
get you zero points because it doesn't actually talk about
synchronization.
There is
an example of how to use the Process Problem Chart here.
The source
code for the above example is provided here.
(This blue highlighted
material was added on August 1st - if it's useful please use it BUT if
you've already got good answers then don't worry about it)
Finding and Fixing Errors With The Semaphore-Based Bounded Buffer Problem
Note/Citing my sources: I obtained the above materials from a subdirectory of the 'Ch6' materials at the publisher's website. Students: Please ignore this link and use the one that's highlighted in yellow, above :)
For each of the following questions please explain whether or not a problem can occur when the provided code is changed.
.release()
were left out.mutex.release();
)
out of the code.full.release();
), or any
other line that contains .release()
.mutex
variable was left out.full.release(); // this
is the old line 63, but now it's line 62
mutex.release();
Answer these questions the same way that you answered questions in the previous part of the lab.
returnForks()
method's
call to first test()
, calling signal would in
fact exit both test()
and returnForks()
.Click here to download the .ZIP file containing the starter project this part of the lab
In this section of the homework assignment you will solve the Dining Philosopher's problem in Java with a monitor-style class.
IMPORTANT NOTE: It is not enough to solve this problem somehow, anyhow. You must solve this problem using a monitor. In particular, you are not allowed to solve this problem using the semaphore-based approach that was explained to you in class.
Before attempting to code up your solution you should of course read the relevant sections of the textbook, and then investigate your options for solving this problem in Java. It would be a good idea to read through the API defined in the starter project and/or the test code in that starter project.
Part A: Investigating Java support for solving the Dining Philosophers problem
For this part you should investigate the following options for solving this problem. For each of the following put write a brief comment about how you could use it to solve the problem, along with a short list of pros & cons for each one.
Please put your answers into a file named DiningPhilosophers.docx (or .pdf, etc), in a section named "Investigating Java Synchronization".
Step B: Explaining your solution
In the same file (DiningPhilosophers.docx) put in a section named "How My Solution Works". In this section you must clearly do two things:
Step C: Write the code
Finally, you need to write up a solution to the problem in Java.
You must download the starter project provided above (in the yellow highlighted link) and implement the DiningPhilosophers class inside the DiningPhilosophers.java file. You must make sure that your code passes all the tests defined in that starter project. There are comments in the starter files explaining what each test does, and the API that you're expected to implement is described here:
Note: You do not need to use IntelliJ to do this lab if you don't want to, but you still need to make sure that your solution passes all the tests provided in the starter project.
Method | Explanation |
public DiningPhilosophers(int
nPhilosophers) |
When the DiningPhilosophers object is created
this constructor is called; the parameter specifies how many
philosophers will be sitting at the table. |
public void takeForks(int i) |
Remember that each 'philosopher' will be represented in Java
as a separate thread. Each thread will call takeForks
when that philosopher wishes to begin eating. It is expected
that each thread will store an 'id number' that it will pass to
takeForks .If a given philosopher cannot eat immediately then it should block, waiting until it's a good time for it to eat. |
public void returnForks(int i) |
When a given philosopher (thread) is done eating it calls this
method to tell the DiningPhilosophers object that it's
finished. |
public int numPhilosophers() |
This method is used by the test code, in order to know how
many philosophers are sitting at the table. Your method return the number of philosophers at the table (this will be the same number as the nPhilosophers parameter that was
passed to the constructor) |
public DiningState getDiningState(int
i) |
This method is used by the test code, in order to verify that
the philosophers are doing the right thing after a given API call.
The possible responses are:DiningState.EATING (if
the philosopher called takeForks AND successfully
picked up the left and right chopsticks, and is not currently
eating).DiningState.THINKING (if the philosopher
is neither eating nor attempting to eat. This will happen
either when the thread has never called takeForks , or
else after calling both takeForks and then
returnForks )DiningState.HUNGRY (if the
philosopher called takeForks but was unable to obtain
both the left and right chopstick, and it now blocked (waiting)
for a chopstick to become available). |
Step D: Debug your code
First, you should aware that the test code is pretty "rough" - it works but is not polished. This is on purpose - most test code used in production environments isn't super-well maintained and so it's good to get used to dealing with 'quick and dirty' code.
Second, you should anticipate that you will need to spend a substantial amount of time debugging your code. It's certainly possible to write the code correctly and have it all work right the first time (and/or to find your problem(s) quickly), but debugging multithreaded code can be very difficult.
Part of what makes debugging this code is just that it's hard to figure out what each thread is doing, and when. It doesn't help that the debugger can alter how things happen (either because of what the debugger is doing, or because the debugger hit a breakpoint and then you stared at the code for a minute)(if you do use a debugger make sure that you search the Internet for advice on how to switch threads, and then MAKE SURE YOU'RE ON THE RIGHT THREAD!!! :) )
I instead recommend adding lots of 'print' statements (aka 'logging') to your code, so that your program will produce a 'trace' or a 'transcript' of what it's done. You can then run the program normally (without the debugger) and examine all the output on the console to try and figure out what happened.
In order to make this easy to do the starter project includes a method named 'Main.TPrint':
public class Main { private static boolean DEBUG = true; // Print the message and the thread that's executing this code // This is used for debugging purposes public static void TPrint(String msg) { if( DEBUG ) System.out.println(Thread.currentThread().getName()+": " +msg); }
This will print out both your message and the thread that is running
this code. If you want to turn off all of these debugging messages
simply set the DEBUG
variable to be false.
A typical example of using the TPrint method is:
public void takeForks(int i) { Main.TPrint( "TakeForks: i=" + i); }
Notice that because the TPrint method is static you need to call it by
listing the name of the class ("Main.TPrint
")
instead of listing an individual object.