~~SLIDESHOW~~
Sometimes you need more than multiple objects; you need multiple subtasks. The Java Virtual Machine supports multi-threading – independent subtasks in the program’s address space.
The slides and notes in this presentation are adapted either from Thinking in Java or Groovy in Action (see Recommended Reading).
An index to the source code for all the examples in this lecture is available.
Thread
run()
run()
method is executed “simultaneously” with other threads in programExample 1: simpleThread.groovy creates five threads, each with a unique id. run() method counts down from 5 to 0.
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture09/simpleThread.groovy
#1: 5 #1: 4 #1: 3 #1: 2 #1: 1 #2: 5 #2: 4 #2: 3 ... #5: 2 #5: 1
Thread
's yield()
method gives hint to JVM that task is complete and another thread can be scheduled.run()
takes too long.yield()
is reached. public class YieldingThread extends Thread { // ... void run() { while(true) { println this if (--countDown == 0) { return } yield() } } // ... }
<html> <table><tr><td><pre> #1: 5 #2: 5 #4: 5 #5: 5 #3: 5 #1: 5 #5: 4 #3: 4 #1: 4 #4: 4 #5: 3 #3: 3 #1: 3 #4: 3 </pre> </td><td> <pre> #5: 2 #3: 2 #1: 2 #4: 2 #5: 1 #3: 1 #1: 1 #4: 1 #2: 4 #2: 3 #2: 2 #2: 1 </pre> </td> </tr> </table> </html>
public class SleepingThread extends Thread { // ... void run() { while(true) { println this if (--countDown == 0) { return } try { sleep(100) // 100 ms } catch (InterruptedException e) { throw new RuntimeException(e) } } } // ... }
Example 3: sleepingThread.groovy.
Method sleep(100)
schedules a time delay of 100 ms. The Thread
is suspended until this time is up. Call to sleep() must be put in a try
block because it could be interrupted before it times out. You should use wait()
rather than sleep()
if you expect the interrupt!
Threads setPriority(int)
.Thread.MIN_PRIORITY
, Thread.MAX_PRIORITY
and Thread.NORM_PRIORITY
. isDaemon()
Thread
can be made into a daemon using setDaemon()
.join()
on another threada
calls b.join()
on thread b
.a
is suspended until thread b
finishes (when b.isAlive()
is false
)join()
can be called with a timeout argument so that if the joined thread does not complete before the timeout, join()
will return earlyThread
class and override run()
.Runnable
interface (also implemented by Thread
)Runnable
interface defines only the run()
method.Runnable
, define a run()
method, pass object of class to new Thread(Runnable)
constructor.Closure
implements runnable.def t = new Thread () {/* closure body */} t.start()
Thread.start{/* closure body */}
Thread.startDaemon() {/* closure body */}
new Timer().runAfter(1000) {/* closure body */}
Example 4: using threads with synchronization for the producer/consumer problem.
The producer pushes integers on a stack, consumer pops items when available. The push/pop actions are reported. Actual sequence is not predictable. We use closures to run something (producing and consuming) and sleep
to slow down the consumer. We introduce a Storage
class that holds the stack and synchronizes access to it. If we try to pop
from an empty stack, we will wait
until the producer has caught up.
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture09/producerConsumer.groovy
Example 5: a responsive user interface.
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture09/responsiveUI.groovy
Explanation: this program simulates a problem often encountered with distributed programs, and programs that have a graphical user interface. In the first class, the while
loop simulates some long running process that the user interface may have launched. Unfortunately, it never terminates, so the input statement at line 10 is never executes: in other words, the user interface has become unresponsive.
In the second example, the class is made into a daemon and the long running process is coded in the run method. This time the user interface is in a separate thread (and after 300 ms) input can be accepted from the user (which in this case allows the current version of d
to be printed).
In interactive programs, the placing of long-running computation (e.g. processing of some distributed process across a network) into a separate thread, is a commonly used technique.
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture09/BankAccount.groovy
Runnable
if (account.balance >= amount) { try { Thread.sleep(500); } catch (InterruptedException ex) { ex.printStackTrace(); }
extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture09/ryanAndMonicaJob.groovy
Monica's check was invalid because Ryan was already making a withdrawal. We need a way to prevent Monica starting a withdrawal if Ryan is already making a withdrawal.
<html>
<table>
<tr>
<td>
<ul>
<li class=“level1”>Associate a lock with withdraw method.</li>
<li class=“level1”>If Ryan or Monica wants to make a withdrawal, he/she must acquire the lock, and keep the key.</li>
<li class=“level1”>If Ryan has the key, only he can access the bank account.</li>
<li class=“level1”>Lock prevents Monica accessing the bank account.</li>
</ul>
</td>
<td>
<ul>
<li class=“level1”>When Ryan has finished the transaction, he unlocks the object and the key becomes available.</li>
<li class=“level1”>Monica can now take the key and lock the bank account to prevent Ryan making a withdrawal.</li>
<li class=“level1”>Lock ensures that only one Thread
has access to shared object at a time.</li>
</ul>
</td>
</tr>
</table>
</html>
synchronized
method runs as an atomic transaction and while running cannot be entered by another thread.Thread
holds the key. extern> http://www.cpjobling.org.uk/~eechris/at-m42/Examples/lecture09/ryanAndMonicaJob2.groovy
static
data membersprivate
, only accessed through methodssynchronized
, entering that method acquires the locksynchronized
method can be called at any time for a particular object synchronized f() { /* ... */ } synchronized g(){ /* ... */ }
synchronized
efficiencydef increment(){ i = balance; balance = i + 1 }
Increment balance
by adding 1 to value AT TIME IT WAS READ rather than CURRENT value.
Thread
A enters increment
method:balance
into variable i
.balance
is 0, so i
is now 0.balance
to the result of i + 1
.balance
is 1.Thread
A re-enters increment
balance
into variable i
.balance
is 1, so i
is now 1.balance
to the result of i + 1
.balance
is 2.Thread
B enters increment
method:balance
into variable i
.balance
is 2, so i
is now 2.balance
to the result of i + 1
.balance
is 3.Thread
B re-enters increment
balance
into variable i
.balance
is 3, so i
is now 3.—-
Scheduler now makes Thread
B runnable (ready to run) rather than running.
Note that this is before balance
has been incremented.
Picking up where it left off:
Thread
A re-enters increment
balance
into variable i
.balance
is 3, so i
is now 3.balance
to the result of i + 1
.balance
is 4.Thread
A re-enters increment
balance
into variable i
.balance
is 4, so i
is now 4.balance
to the result of i + 1
.balance
is 5.And picks up exactly where it left off:
balance
to the result of i + 1
.—-
i
had value 3 when Thread
B was stopped. Value of 3 + 1 = 4
passed to balance
. Update done by A is completely overridden. Hence “Missing Update”. A common problem in distributed systems e.g. database transactions.
synchronized void increment() { i = balance balance = i + 1 }
The assignment and increment code will run to end.
Thread A
attempts to enter increment
method.synchronized
code, so get the key for this object.balance
into variable i
.balance
is 0, so i
is now 0.balance
to the result of i + 1
.balance
is 1.increment
method. Gets the key. balance
into variable i
.balance
is 1, so i
is now 1.Thread
B attempts to enter increment
method.synchronized
so we need to get the key.Thread
B is “blocked” (waiting for key to become available).balance
to the result of i + 1
.balance
is 2.Thread B
attempts to enter increment
method.synchronized
so we need to get the key.balance
into variable i
.i
is now 2.balance
to the result of i + 1
.balance
is 3.synchronized
objectssynchronized
methods, but you can also: synchronized(syncObject) { // This code can only be accessed // by one thread at a time }
Four states of a thread:
run
methodsleep(milliseconds)
.suspend( )
. It will not become runnable again until the thread gets the resume( )
message.wait( )
. It will not become runnable again until the thread gets the notify( )
or notifyAll( )
message.synchronized
method on another object and that object's lock is not available.Thread
B enters synchronized
method of object bar
and gets the key.Thread
B tries to enter a synchronized
method of object foo
, but can't get that key (A has it).foo
's key to become available.bar
key.Thread
A wakes up and tries to enter a synchronized
method on object bar
, but can't get that key because B has it.bar
's key to become available.Thread
A can't run until it gets bar
key that B is holding.foo
key that A is holding.