Title: Java Threads
1Java Threads
2Main Topics
- Concurrency, processes and threads
- Defining and running Java threads
- Thread states, scheduling and termination
- Interleaving
- Synchronization
- Deadlocks
- Coarse and Fine-Grain Locking
- Wait and Notify
- Deprecated thread methods
3Concurrent Systems
- A system is concurrent if it can perform several
activities simultaneously - in parallel, at the same time
- execution of activities can overlap
- Concurrent systems are everywhere
- human body, university, car, personal computer,
- Some forms of concurrency in computing
- multiple processes on
- different machines, a multi-processor machine
(e.g. Dual Core), a single-processor machine - different threads within one process
- Web browser, Web server
- Real versus apparent (virtual) concurrency
4Operating System Processes
- Modern OS like Unix or Windows support multiple
processes (multitasking) - c.f. Windows Task Manager -gt Processes
- typing java MyProg creates a new process that
runs MyProg in a JVM. - Each process has an independent address space
processes do not share variables - Managed by the operating system
- OS ensures processes do not interfere
(isolated) - need special mechanisms for communication like
signals, sockets, remote method invocation - Switching processes is relatively slow
- requires a lot of copying and loading
5(Java) Threads
- Lightweight concurrent processing
- A process can have many threads
- Each thread is an independent flow of control
- Threads DO share the same address space
- so separate threads running in the same process
can share variables (communication is easy), but - threads can also easily disturb each other.
- Java threads are managed by JVM
- scheduler activates/deactivates threads
- switching threads is relatively fast
- Each program starts with at least one thread
(main) - http//java.sun.com/docs/books/tutorial/essenti
al/threads/
6Defining and Running Threads
- Define class that implements Runnable
- Runnable has one method void run(). This method
defines the thread behaviour, equivalent of
main() method. The thread terminates when run()
terminates. - Construct object of runnable class
- Construct thread from object
- Start thread
- Alternatively, can also obtain Thread objects by
extending class Thread and overriding run() - This approach can save some lines of code.
- Disadvantage
- extends Thread, so no other subclassing is
possible
7Defining GreetingThread
- public class GreetingThread implements Runnable
- private String greeting
- private static final int REPETITIONS 8
- private static final int DELAY 100
- public GreetingThread(String aGreeting)
- greeting aGreeting
-
- public void run()
- try for (int i 1 i lt REPETITIONS
i) - System.out.println(i " " greeting)
- Thread.sleep(DELAY)
-
- catch (InterruptedException exception)
-
-
8Creating Concurrent GreetingThreads
- public static void main(String args)
- Runnable r1 new GreetingThread("Hello,
World!") - Runnable r2 new GreetingThread("Goodbye,
World!") - Thread t1 new Thread(r1)
- Thread t2 new Thread(r2)
- t1.start()
- t2.start()
9GreetingThread Two Sample Program Runs
1 Hello, World! 1 Goodbye, World! 2 Hello,
World! 2 Goodbye, World! 3 Goodbye, World! 3
Hello, World! 4 Hello, World! 4 Goodbye,
World! 5 Goodbye, World! 5 Hello, World! 6
Hello, World! 6 Goodbye, World! 7 Goodbye,
World! 7 Hello, World! 8 Hello, World! 8
Goodbye, World!
- 1 Hello, World!
- 1 Goodbye, World!
- 2 Hello, World!
- 2 Goodbye, World!
- 3 Hello, World!
- 3 Goodbye, World!
- 4 Hello, World!
- 4 Goodbye, World!
- 5 Hello, World!
- 5 Goodbye, World!
- 6 Hello, World!
- 6 Goodbye, World!
- 7 Hello, World!
- 7 Goodbye, World!
- 8 Goodbye, World!
- 8 Hello, World!
10Thread State Diagram Life Cycle
11Thread States
- Each thread has a state and a priority
- Reasons for blocked state (non runnable)
- sleeping (its sleep() method is invoked)
- waiting for I/O
- waiting to acquire a lock
- waiting for notification
- Unblocks only if the reason for block goes away
- in case of sleep() specified time has elapsed
- in case of I/O I/O has completed
- in case of waiting for lock acquired lock
- in case of waiting for notification got
notification
12Thread Scheduling
- Scheduler de-activates a new thread if
- a thread with higher priority becomes runnable
- a thread yields because
- run() method exits, called yield(), has blocked
itself or - for OS with time-sliced threads, it time slice is
over - Scheduler looks only at runnable threads
- prefers threads with higher priority, also aging
schemes - JVM schedulers try to be fair
- but on machines without pre-emptive time-slicing
for threads, a selfish thread could hog the CPU.
- Ensure balanced progress of threads by doing CPU
intensive work in small chunks - less significant for threads which do lots of
I/O, or in case of time-slicing for threads.
13Terminating Threads
- Thread terminates when run() exits
- sometimes necessary to terminate a running thread
- Don't use deprecated stop() method
- instead interrupt thread by calling interrupt()
- Thread should cooperate
- Normally exit after cleaning up
- Threads should check for interruptions by
- calling isInterrupted() or
- performing a sleep/wait and catching
InterruptedException, see GreetingThread above. - Threads are not restartable.
- Method isAlive() returns true if a thread has
been started but not terminated.
14Handling Interrupts in Threads
- public class MyRunnable implements Runnable
- public void run() try while (...) //
do work Thread.sleep(...) catch
(InterruptedException e) // clean up
15 Concurrency Interleaving
- Consider threads t1, t2 with run() methods
consisting of two atomic statements each - t1 public void run() a b
- t2 public void run() c d
- Possible execution sequences after starting both
threads (t1.start() t2.start()) - (a b c d), (a c b d), (a b d c)
- (c d a b), (c a d b), (c a b d)
- All you know is a before b and c before d
- even that could be wrong due to compiler
optimizations, see http//www.jcp.org/en/jsr/detai
l?id133
16- public class SharingThread implements Runnable
- public static int shared
- public int id
- static final int REPETITIONS 100000000
- SharingThread(int id) this.id id
- public void run()
- for (int n 1 n lt REPETITIONS n)
- shared id
- if (shared ! id)
- System.out.println("n " n)
- break
-
-
-
17Running Concurrent SharingThreads
public static void main(String args)
Thread t1 new Thread(new SharingThread(1))
Thread t2 new Thread(new SharingThread(2)) t
1.start() t2.start()
- First run n 52344748
- Second run n 26988321
- Third run n 15230285
- ...
- Seventh run n 24764904
18Interleaving SharingThread
- Why is (shared ! id) possible?
- Because there is nothing that forbids the second
thread from changing the value of variable shared
just after it has been set by the first thread,
and before condition is evaluated -
- SharingThread(1) shared 1
- SharingThread(2) shared 2
- SharingThread(1) if (shared ! id)
19Class CounterThread
public class CounterThread extends Thread
static int total 0 int n public
CounterThread(int n) this.n n
this.start() public void inc()
total public void run() for (int
i 0 i lt n i) inc() public static void
main(String args) throws Exception
20Creating Concurrent CounterThreads
- public static void main(String args) throws
Exception - int x Integer.parseInt( args0 )
- System.out.println( "Running with x " x )
- ElapsedTimer t new ElapsedTimer()
- Thread t1 new CounterThread( x )
- Thread t2 new CounterThread( x )
- t1.join()
- t2.join()
- System.out.println( t " total "
total )
21Join
- t.join() waits for thread t to terminate before
proceeding - t.join(maxTime) waits at most for maxTime ms
- This is useful when a master thread sends of a
slave to do some work. - see TimeOutReader later
- In CounterThread.main(), the joins are necessary
in order to determine how long it has taken until
both threads are done.
22Results from CounterThread Runs
- Running with x 1000000078 ms elapsed total
20000000 - Running with x 1000000078 ms elapsed total
19298155 - Running with x 1000000078 ms elapsed total
20000000 - Running with x 1000000079 ms elapsed total
20000000 - Running with x 1000000062 ms elapsed total
17874631
23Time-Sliced Thread Interleaving Incrementing a
shared variable
Thread t1
Thread t2
total
0
fetch total
0
fetch total
total
1
total
1
return
1
1
return
1
24Thread Safety
- When two or more concurrent threads access a
common variable potential trouble! - problem caused by interleaved reading and writing
of shared objects, reading on its own is ok - so called race conditions
- Missing atomicity of statements
- what if one thread yields control when it is
half-way through updating a shared object? - Leads to bad kind of programming errors
- program works nearly always, errors often only
show up after a long time/long run - Java approach object locks/synchronisation
25Object Locks and Synchronised Methods
- Each object has a lock
- Locks are associated with actual objects, NOT
with references to objects - Threads can lock, unlock object
- No other thread can lock object until unlocked
- synchronized is a Java keyword
- synchronized method locks implicit parameter
- So no other thread can call another synchronized
method on the same object - provides mutual exclusion for critical code
that accesses shared resources/ shared variables.
- Place concurrency-sensitive code regions inside
synchronized methods - public synchronized void myMethod ()
26Visualizing Locks Horstmann OOPD
- Object phone booth
- Thread person
- Locked object closed booth
- Blocked thread person waiting for booth to
open - Monitor/lock system that ensures at most one
person is in the booth at any time
27Synchronized CounterThread
- Simply insert the keywords in bold
- public static synchronized void inc()
- Now we get the following results
- Running with x 10000000
- 4156 ms elapsed total 20000000
- Running with x 10000000
- 4952 ms elapsed total 20000000
- Running with x 10000000
- 4812 ms elapsed total 20000000
28Why Static?
- Without the static modifier, we synchronize on
instances of the class - I.e. we have two locks one for c1 and one for
c2. - Thus there is no contention for the locks, and no
protection for the static variable total. - With the static modifier, the lock is on the
CounterThread.class object. - There is only one of these in the JVM, and the
static variable total is now properly protected
29Synchronized Version (x)
Thread t1
Thread t2
total
0
fetch total
1
total
return
1
fetch total
1
total
2
2
return
2
30Performance of synchronized
- The code now always returns the correct answer
- fixing the critical race problem of the incorrect
version - But it is about seven times slower than the
incorrect, unsynchronized version! - Only use synchronization when you need it!
- When you do need it, be sure to use it!
- Note method sleep() does not cause a method to
give up its monitor on an object.
31Block Synchronization
- Can also synchronise a block of code by
associating it with an object that gets
locked/unlocked. - Suppose we have two bank account objects
- use synchronisation to avoid interference when
transferring money from one account to another. - synchronized (a1)
- synchronized (a2)
- a1.transfer( -amount )
a2.transfer( amount ) -
32Block Synchronization (contd)
- Suppose we now have to book a set of resources
- Must either book all of them
- Or none of them
- Dont know how many in advance
- Cannot use nesting solution from previous slide
- Simple iteration does not work e.g.
- int nFree 0
- for (Resource r rs)
- synchronized(r)
- if (r.free()) nFree
-
-
- Why not?
33Recursive solution
- Acquire lock on each resource
- And then call the same method recursively
- Until all locks have been obtained
- boolean allFree(IteratorltResourcegt ir)
- if (ir.hasNext()) Resource r ir.next()
- synchronized(r) if (r.free())
- return allFree(ir) // note recursive
call else - return false
-
- else // a realistic example would do
some action here return true -
-
34Java Semaphores
- Directly control access to shared resources
- java.util.concurrent.Semaphore
- Usage construct one with the number of
concurrent accesses allowed (e.g. 1) - Semaphore sem new Semaphore(1)
- To enter the protected zone
- sem.acquire()
- When exiting the protected zone
- sem.release()
- Offers an alternative to synchronized blocks for
fine-grained locking.
35Deadlock (Starvation)
- Dining philosophers example
- philosophers sit at a round table
- eat spaghetti with two forks
- assume a philosopher first picks up fork to the
right, then picks up fork to the left, then eats,
then drops forks - what if everyone picks up right fork in parallel
at start? - Deadlock situation with two threads/locks
- Thread 1 acquires lock on object A
- Thread 2 acquires lock on object B
- Thread 1 waits for lock on object B
- Thread 2 waits for lock on object A
- Deadlock avoidance
- ensure deadlocks are impossible
36Coarse-grained Locking
- A thread obtains the lock (the bold outline) on
an entire set of resource objects - This locks out any other thread from accessing
any of the objects in the set. - Little danger of deadlock.
- Potentially inefficient if threads could do some
work independently - for example, one thread could do computations
while another waits for file download.
Res. 1
Res. 2
Res. 3
Res. n
37Fine-grained Locking
- A thread just obtains the locks on the particular
objects it needs for a transaction - Potentially more efficient than coarse-grained
whenever concurrent work is possible - less blocking of threads
- More danger of deadlock due to individual locks.
- Q is deadlock possible for synchronized
CountTimer.inc() ?
Res. 1
Res. 2
Res. 3
Res. n
38Dining Philosophers
39Coarse-grained Locking
- Implementing a coarse-grained locking strategy
for the dining philosophers is straightforward - The synchronization is done on the entire set of
chopsticks - so at most one philosopher can access chopsticks
- This prevents deadlock
- But as we can see from running the applet, it
makes for slow eating - if we interpret the philosophers as computational
resources and eating as doing work, then at
most one resource is actually working at any
given time.
40Fine-grained Locking
- Now we lock at the chopstick level i.e. at
individual resource level - the synchronisation ensures that no two
philosophers can pick up one chopstick at the
same time - BUT need to be careful about how we request the
locks - Otherwise, could end up in a state of deadlock
- Simple deadlock avoidance strategy
- restrict order of requests
- philosopher 1 takes right fork first, then left
forkall other philosophers take left fork
first, then right fork - other strategies are possible
41wait, notify, notifyAll
- Given an Object obj
- obj.wait() causes the current thread to wait
until - another thread calls obj.notify() or
obj.notifyAll(). - These methods can only be called by a thread that
owns the monitor on this object. - a thread acquires the monitor on an object by
entering a synchronized method/block on that
object. - A waiting thread is blocked until it is notified.
- notifyAll() unblocks all threads waiting on this
object - wait(long timeout) waits specified time for
notification - if no notification has arrived within time, then
the thread tries to regain monitor on object.
42Using wait and notify
- Support for communication between threads
- For example consider a program that waits for
user input before proceeding - Lets look at two examples
- first a command line version
- then the GUI version using a PauseButton
43Command-line PauseIO
- package cc438.exercises
- public class PauseIO
- public static void main(String args) throws
Exception - System.out.println("Press ltentergt to
continue") - System.in.read()
- System.out.println("Thank you for
continuing") -
-
44Using a PauseButton
-
- public static void main(String args) throws
Exception - PauseButton pauser new PauseButton()
- new JEasyFrame( pauser, "Click to
proceed", false ) - pauser.pause()
- System.out.println("Thank you for
continuing") -
45- // package and import omitted
- public class PauseButton extends Button
- implements ActionListener
- public PauseButton() // this labels the
Button - super("Click to proceed")
- addActionListener( this )
-
- public synchronized void pause() throws
Exception - wait()
-
- public synchronized void actionPerformed(
ActionEvent e) - System.out.println("Button Clicked")
- notifyAll()
-
-
- C\srcgt\jdk5\bin\java test.PauseGui
- Button Clicked
46How the PauseButton Code Works
- After creating a PauseButton, it gets added as a
component to a Frame - JEasyFrame is just a convenient subclass of Frame
- we then call the pause() method on the Button
- The PauseButton class extends java.awt.Button
- The wait() and notify() calls are performed in
synchronized methods of PauseButton - pause() calls wait()
- notifyAll() is called by the GUI thread in
response to a user clicking the button.
47Thread Priorities
- Each thread has a priority, ranging between
MIN_PRIORITY (1) and MAX_PRIORITY(10) - By default, each new thread has the same priority
as the one that started it. - The initial thread associated with main() by
default has priority Thread.NORM_PRIORITY(5) - Priorities affect how threads in the runnable
state are activated for running. - Priorities can be used for tweaking performance
- improve responsiveness of program
- Programs should not rely on priorities for
running correctly.
48Thread Performance
- Several overheads to consider
- Thread creation / destruction
- The cost of creating and destroying threads
- Thread load
- How many threads can run concurrently before
running out of memory? - Synchronization
- The cost of acquiring / releasing object locks
- Thread activation
- Cost of stopping / starting threads
49Daemon threads
- Normally created threads are called user threads.
- Method setDaemon(boolean on) marks
- a daemon thread (ontrue) or a user thread (on
false). - this method must be called before the thread is
started. - by default, a child thread inherit parent daemon
status. - Daemon threads are supposed to be servants to
user threads, so the JVM exits when the only
threads still alive are daemon threads. - this will terminate all the daemon threads
- Example GUI event threads are daemon threads
- Feature not all that useful but can save some
manual thread termination code.
50Deprecated Thread Methods
- Thread.stop() was deprecated as stopping a thread
causes it to unlock all monitors that it has
locked. - danger of inconsistent program states unless
programs keep checking for stopped threads
(impractical) - Thread.suspend() and Thread.resume() were
deprecated as they are very deadlock prone. - resource blocks if suspended thread holds lock
- Use deprecated methods at your peril!!
- Instead of stop(), prefer interrupt()
- let the thread decide how to react to an
interrupt - Instead of suspend() /resume(), use
wait()/notify(). - Further details at http//java.sun.com/j2se/1.5.0
/docs /guide/misc/threadPrimitiveDeprecation.html
51Thread Summary
- Java provides straightforward ways to write
multithreaded programs - implements Runnable, or extends Thread
- Thread work done within its run() method
- Keyword synchronized to lock methods or blocks
of code - wait() and notify(), notifyAll() to communicate
between blocking threads (see PauseGui) - t.join() used to wait for the death of thread t