This is part 1 of a two part assignment in which you will implement a simulation of a process scheduler using Java threads.
The simulation will consist of:
In this assignment, you will implement a simple first-come, first-serve (FCFS) scheduler for (simulated) processes that do no (simulated) I/O. In other words, each process will consist of a single (simulated) CPU burst. Instances of the class named Job will be used to simulate the processes. Job will be a subclass of Thread. To test your code I will provide my own factory to your Submittor to see how well your scheduler executes my simulated processes. The end result should be a Gannt chart based on the actual running times of the threads. Output should be to the console, not a file.
Your simulation should be of an old-school, single procossor, single-threaded system. Because we're actually running the simulation on modern hardware with multiple cores and multi-threading, we've got to manage things carefully. In particular, you should manage it so that whenever the kernel thread is running, there is no Job thread running, and vice versa. A fairly straightforward way to accomplish this is to have the behavior between the kernel and each of the Jobs mimic that of a producer/consumer using a bounded buffer of size 1. To do this my solution uses Java's ReentrantLocks and their associated Condition objects. These work very much like the monitors (and their condition variables) discussed in the textbook. I use Java's synchronized methodology elsewhere, but with ReentrantLocks I can create a separate Condition object for each Job. All the Jobs and the kernel hold the same lock, L. Consequently, only one of them can be running at a time. By using await() and signal() on the Conditions you can pass control back and forth between the kernel and the Jobs.
For an excellent discussion of ReentrantLocks and Conditions, please see Jeff Friesen's article at www.javaworld.com.
Here's an outline of how this works.
To implement the ready queue for a FCFS scheduler, you might consider using the java.util.concurrent.ConcurrentLinkedQueue class, though you could also simply use an ArrayList and synchronize the appropriate methods.
To complete the implementation of this project, start by examining the code that I have provided. Note which methods are available for each class. I have marked with "TO_DO" all the places that you need to complete the coding.
While they are running, Jobs should be invoking their associated JobWorkable's doWork method regularly, so that it can do something useful. See the sample output, below. There the doWork invocations are generating a simple line of output. The given implementation of Job.run does this.
Note the remark above, "whenever the kernel thread is running, there [should be] no Job thread running, and vice versa". A result of this is that your threads should not rely on busy-waiting for coordination. For example, Scheduler.blockTilThereIsAJob should not use bush-waiting til block processing until there is a Job to schedule. Instead, use either wait/notify with synchronized or use await/signal with Condition objects. The use of busy-waiting will deny you full credit for the project.
My boilerplate code for the driver, RunScheduler.java, is complete. If you'd like to alter the file, that is fine, but I will execute your program by running the main() of this file.
The input file must be named "scheduleInput.txt". Each line of this file is of the form:
jobID delayTillSubmission CPUburst
The first argument will be an integer, and must somehow be used in the name of the Job (how is up to you). The second argument is the number of milliseconds that should elapse from the submission of the Job corresponding to the previous input line until this Job is submitted to the SystemSimulator via AddNewProcess. (If this is the first line of the file, then delayTillSubmissionshould be measured from the start time of the SystemSimulator itself. The CPUburst is the number of milliseconds that this Job should run until it terminates. Such time, of course, excludes any spent on the ready queue.
Here is a time-flow diagram that shows when the various threads implementing the simulation are running.
The output must end with some kind of a Gannt chart that identifies when each job was running. The bottom of this sample output provides an example, though your method could be different. The Gannt chart should start with the execution of the first job to arrive, and end with the termination of the last job specified in the input file. Note the Gannt chart must be able to show "idle" periods (if they exist): where no jobs are currently running, but the input file specifies that more are yet to arrive. The Gannt chart's times should start at 0.
For example, suppose the input file was:
1 0 200
2 300 300
3 300 300
4 100 300
(Note that job #1 should finish at around time 200, but job #2 doesn't arrive until time 300.) Then the resulting Gannt chart might look something like (because you are using real-time timers, the numbers may fluctuate slightly):
GANNT CHART: BurstStart BurstEnd JOB
0 1 IDLE
2 211 1
211 302 IDLE
302 608 2
609 910 3
911 1212 4
1212 1212 FINISHED
[As of Winter2025] The output must include messages when jobs are submitted to the system. In the sample input that should occur, roughly, at times 0, 300, 600, and 700. Those messages may sometimes interleave with those being generated by the Jobs. For example, in the sample input, Job 4 should arrive during the execution of Job 3. This is reflected at line 63 (containing the substring "to the readyQ") of the sample output .
Can be found at this link.
Submit to the assignment's drop box a zip file containing
The source code can either be all of your .java files, or the Eclipse folder you used.