Run method discussion
By: Nicholas Duchon, Feb 29, 2016
See the Discussion following the code
1 public void run () {
2 long time = System.currentTimeMillis();
3 long startTime = time;
4 long stopTime = time + 1000 * jobTime;
5 double duration = stopTime - time;
6
7 synchronized (worker.party) { // party since looking forward to P4 requirements
8
while (worker.busyFlag) {
9
showStatus
(Status.WAITING);
10 try {
11
worker.party.wait();
12 }
13
catch (InterruptedException e) {
14
} // end try/catch block
15
} // end while waiting for worker to be free
16
worker.busyFlag = true;
17 } // end sychronized on worker
18
19 while (time < stopTime && noKillFlag) {
20
try {
21
Thread.sleep (100);
22
} catch (InterruptedException e) {}
23
if (goFlag) {
24
showStatus
(Status.RUNNING);
25
time += 100;
26
pm.setValue ((int)(((time - startTime) / duration) * 100));
27
} else {
28
showStatus
(Status.SUSPENDED);
29
} // end if stepping
30 } // end runninig
31
32 pm.setValue (100);
33 showStatus (Status.DONE);
34 synchronized (worker.party) {
35
worker.busyFlag = false;
36
worker.party.notifyAll
();
37 }
38
39 } // end method run - implements runnable
Discussion:
- A key observation about this code and locks:
- exactly what object is locked is less important than that
ALL the threads that have shared access to a resource (class
attribute, for example), check the same object for that
object's lock before accessing the shared resource(s).
- This method is the run method required by the "Job implements
Runnable" declaration of the first line of the Job class.
- The thread is created and started at the end of the Job
constructor.
- The basic method works this way:
get the worker
do the job
release the worker
- (lines 7-17) Get the worker
- this code is synchronized to make sure no other thread
changes the worker.busy flag while THIS thread is trying to
change it (from not busy to busy)
- This is a typical kind of code that might lead to a race
condition
- get value
- check value
- change value
- write value
- So to get exclusive access to the SHARED attribute (busy),
the code syncs on some object
- Here we sync on the Party that holds the Creature that has
the attribute of interest
- We could just sync on the Creature, but in Project 4 we
will want to check out the resources available to the entire
Party
- This, of course, is somewhat less efficient in Project 3
since this code will wake on other creatures within this
Party.
- lines 8-15
- this while loop continues to go until the Creature we need
is free
- the while part is a kind of spin lock
- BUT, the wait inside the loop releases the lock until some
other thread is done
- So, this thread is NOT hogging the CPU while waiting to
get the Creature
- Releasing the lock is the difference between running this
code at 5% of the CPU or 95%.
- You can check this with jconsole
- line 16
- The thread can only get here if no other thread is using
the Creature, right now!
- Since THIS thread holds the lock on the Party of the
Creature this job needs:
- THIS thread gets to mark the Creature busy
- ALL other threads for this Party will now see that this
Creature is busy.
- line 11
- we didn't get the flag
- some other thread must be using this worker
- there is no reason to spin on the flag, so:
- go into a wait state, releasing the lock on the party
- wait until some other thread signals that that thread/job
is done - that thread will release a creature and
signal all the threads waiting for a signal on this party
- at which point, this thread wakes up and goes into the
while loop again
- line 18 - the thread will only get here if it is the ONLY
thread using this Creature
- At this point, ALL other threads will perceive that this
Creature is busy
- The exclusive setting of the busy flag is the whole point
behind the locking and synchronization.
- (lines 19-30) Do the job
- notice that NOTHING about this part of the code is shared
with any other thread, which is why we don't need to protect
this code or its variables with a synchronize block or any
locks.
- line 21 -
- pause the cpu,
- pretend the thread is doing something
- mostly to slow things down to human speeds
- This thread wakes up every 100 ms (1/10 of a second):
- this makes the display pretty smooth for humans
- a delay of 1 second is very noticeable, so too long
- on the other hand, 100 ms is a very long for a
computer running at 3 GHz
- leaves lots of time for other threads to run
- upon waking:
- if go state:
- update JProgressBar
- update how much time this thread has worked
- else
- end if
- check of done or cancelled
- lines 24-26
- update the display
- JProgressBar
- status and control buttons
- (lines 34-37) Release the worker
- line 34 - synchronize
- change the worker status, making sure no other thread with
access to the SHARED variable "busy" can access (change)
this variable while this thread is doing that.
- Line 36
- the code MUST hold a lock or mutex to call a notify or
notifyAll
- the notifies will go to other threads waiting on the lock
this code holds - worker.party
- so the lock is on the party of the creature of this job -
not any other party
- this lock is NOT on the creature
- As noted above, this is going to work because nowhere else
in the code is a lock placed on a creature
- ALL locks are on parties
- these locks should be held for VERY short time periods.