Title: Java Threads 2
1Java Threads 2
- Web Programming course
- Dan Goldwasser
- dgoldwas_at_cs.haifa.ac.il
2What are threads?
- A thread in computer science is short for a
thread of execution or a sequence of
instructions. Multiple threads can be executed in
parallel on many computer systems. - This multithreading generally occurs by time
slicing (where a single processor switches
between different threads) or by multiprocessing
(where threads are executed on separate
processors). - Threads are similar to processes, but differ in
the way that they share resources. - (From Wikipedia, the free encyclopedia)
3Java implementation of threads
- We shall look at three version of thread
instances - Interface Runnable
- Class TimerTask
- Class Thread
4Hmmm
- Scheduling introduces some new concepts
- non-determinism
- race condition
- concurrency
- A mechanism for synchronizing the threads could
be useful
5Non Determinism Example
- class My_thread extends Threadprivate int
field_1 0private int field_2 0public
void run() setDaemon(true) // this thread
will not keep the app alive while( true
) System.out.println( " field_1" field_1"
field_2" field_2 ) sleep(100) - synchronized public void modify( int
new_value ) - field_1 new_value field_2
new_value - My_thread test new My_threadtest.start()//.
..test.modify(1)
field_10, field20field_10,
field21 field_11, field21
6Synchronizing threads
- So far we've seen independent, asynchronous
threads. - Each thread contained all the data and methods
required for its execution and didnt require any
outside resources or methods. - The threads in those examples ran at their own
pace without concern for the state or activities
of any other concurrently running threads. - This is usually not the case, in many situations
concurrently running threads do share data and
must consider the state and activities of other
threads - Some synchronization between the threads is
needed
7Producer - consumer
- the producer generates a stream of data that a
consumer uses - Because the threads share a common resource, they
must be synchronized.
producer
consumer
8Example 1 - producer
- public class Producer extends Thread
- private CubbyHole cubbyhole
- private int number
-
- public Producer(CubbyHole c, int number)
- cubbyhole c
- this.number number
-
- public void run()
- for (int i 0 i lt 10 i)
- cubbyhole.put(number, i)
- try sleep((int)(Math.random() 100))
- catch (InterruptedException e)
-
9Example 1 - consumer
- public class Consumer extends Thread
- private CubbyHole cubbyhole
- private int number
-
- public Consumer(CubbyHole c, int number)
- cubbyhole c
- this.number number
-
- public void run()
- int value 0 for (int i 0 i lt 10 i)
- value cubbyhole.get(number)
-
-
10Producer consumer
- Synchronization is done by writing-reading from
CubbyHole instance - Problems
- Producer is quicker than the Consumer
- Consumer is quicker than the Producer
- A race condition is a situation in which two or
more threads or processes are reading or writing
some shared data, and the final result depends on
the timing of how the threads are scheduled
11Cubbyhole first attempt
- public class CubbyHole
- private int contents
- private boolean available false
- public int get(int who)
- while (available false)
- available false
- System.out.println("Consumer " who "
got " contents) - return contents
-
- public void put(int who, int value)
- while (available true)
- contents value
- available true
- System.out.println("Producer " who "
put " contents) -
-
producer
consumer
12Cubbyhole first attempt
- public class CubbyHole
- private int contents
- private boolean available false
- public int get(int who)
- while (available false)
- available false
- System.out.println("Consumer " who "
got " contents) - return contents
-
- public void put(int who, int value)
- while (available true)
- contents value
- available true
- System.out.println("Producer " who "
put " contents) -
-
producer
consumer
13synchronization
- The Producer and the Consumer must be
synchronized in two ways - The two threads must not simultaneously access
the CubbyHole. When an object is locked by one
thread and another thread tries to call a
synchronized method on the same object, the
second thread will block until the object is
unlocked. - The two threads must do some simple coordination.
That is, the Producer must have a way to indicate
to the Consumer that the value is ready, and the
Consumer must have a way to indicate that the
value has been retrieved. The Object class
provides the methods - wait, notify, and notifyAll
14Synchronized code
- Within a program, the code segments that access
the same object from separate, concurrent threads
are called critical sections - A critical section can be a block or a method and
is identified with the synchronized keyword. - In the producer-consumer example, the put and
get methods of CubbyHole.java are the critical
sections. The Consumer should not access the
CubbyHole when the Producer is changing it, and
the Producer should not modify it when the
Consumer is getting the value.
15Example 2 - CubbyHole
- public class CubbyHole
-
- private int contents
-
- private boolean available false
- public synchronized int get(int who) ...
- public synchronized void put(int who, int value)
... -
16Wait\Notify
- The two threads must also be able to notify one
another when they've done their job - notifyAll() - wakes up all threads waiting on the
object in question . The awakened threads compete
for the lock. One thread gets it, and the others
go back to waiting - notify() - arbitrarily wakes up one of the
threads waiting on this object - wait() - Waits indefinitely for notification.
(This method was used in the producer-consumer
example.) - wait(long timeout) - Waits for notification or
until the timeout period has elapsed. timeout is
measured in milliseconds.
17Example 3 wait\notify
- public synchronized int get()
-
- while (available false)
- try
- //wait for Producer to put value
- wait()
-
- catch (InterruptedException e)
-
- available false
- //notify Producer that value has been
retrieved - notifyAll()
- return contents
-
18Example 3 wait\notify
- public synchronized void put(int value)
-
- while (available true)
- try
- //wait for Consumer to get value
- wait()
-
- catch (InterruptedException e)
-
- contents value
- available true
- //notify Consumer that value has been set
- notifyAll()
-
19Threads Pool
- Many server applications are oriented around
processing a large number of short tasks that
arrive from some remote source - One simplistic model for building a server
application would be to create a new thread each
time a request arrives and service the request in
the new thread. - Overhead of creating a new thread for each
request is significant - Active threads consume system resources. Creating
too many threads in one JVM can cause the system
to run out of memory or thrash due to excessive
memory consumption.
20Threads Pool
- Motivation
- Re-use of existing threads (Thread creation is
expensive) - Control the number of threads running
concurrently (controlling Starvation)
1. Create a number of threads and add the threads
to the pool
What if There are more requests than threads ?
2. Wait for a request
t2
t2
t1
3. Assign a thread to handle the request
t4
t3
4. Return the thread to the pool
21Threads Pool
- public class WorkQueue
- private final int nThreads
- private final PoolWorker threads
- private final LinkedList queue
- public WorkQueue(int nThreads)
- this.nThreads nThreads
- queue new LinkedList()
- threads new PoolWorkernThreads
- for (int i0 iltnThreads i)
- threadsi new PoolWorker()
- threadsi.start()
-
-
- public void execute(Runnable r)
- synchronized(queue)
- queue.addLast(r)
- queue.notify()
-
22private class PoolWorker extends Thread
public void run() Runnable r
while (true)
synchronized(queue) while
(queue.isEmpty()) try
queue.wait()
catch
(InterruptedException ignored)
r (Runnable)
queue.removeFirst()
// If we don't catch RuntimeException,
// the pool could leak threads
try r.run()
catch
(RuntimeException e) // You
might want to log something here
23Risks of thread pools
- Deadlock thread pools introduce another
opportunity for deadlock, where all pool threads
are executing tasks that are blocked waiting for
the results of another task on the queue, but the
other task cannot run because there is no
unoccupied thread available. - Resource thrashing Threads consume numerous
resources, including memory and other system
resources. In addition, the JVM will likely
create a native thread for each Java thread,
which will consume additional system resources. - If a thread pool is too large, the
resources consumed by those threads could have a
significant impact on system performance. - Thread leakage A significant risk in all kinds of
thread pools is thread leakage, which occurs when
a thread is removed from the pool to perform a
task, but is not returned to the pool when the
task completes. One way this happens is when the
task throws a RuntimeException or an Error. If
the pool class does not catch these, then the
thread will simply exit - Tasks that permanently stall, (potentially
wait forever for resources or for input from
users) can also cause the equivalent of thread
leakage. Such tasks should either be given their
own thread or wait only for a limited time. - Request overload It is possible for a server to
simply be overwhelmed with requests. In this
case, we may not want to queue every incoming
request to our work queue, because the tasks
queued for execution may consume too many system
resources and cause resource starvation.
24Starvation and Deadlocks
- a program in which several concurrent threads are
competing for resources, you must take
precautions to ensure fairness. - A system is fair when each thread gets enough
access to limited resources to make reasonable
progress. - A fair system prevents starvation and deadlock ,
starvation occurs when one or more threads in
your program are blocked from gaining access to a
resource and, as a result, cannot make progress. - Deadlock, the ultimate form of starvation, occurs
when two or more threads are waiting on a
condition that cannot be satisfied. Deadlock most
often occurs when two (or more) threads are each
waiting for the other(s) to do something
25Dining Philosophers
How can this be solved?
26Deadlock example
Thread T2
synchronized (y)
synchronized (x) reload (y) .
merge (x,y)
Thread T1
synchronized (x)
synchronized (y)
configure (x)
modify (x,y)
Solution Assign a numeric id value for each
resource Each thread should request dead-locks
in incremental order.
27Summary
- Why is synchronization between threads needed?
- What is the producer-consumer model?
- What is the synchronized keyword? What is being
locked? - How do threads communicate?
- What is the dining philosophers problem? Do you
recognize a simpler version of it?
28 29Extra samples - pipes
- import java.io.
- public class PipedBytes extends Object
-
- public static void writeStuff(OutputStream
rawOut) - try
- DataOutputStream out new DataOutputStream(
- new BufferedOutputStream(rawOut))
-
- int data 82, 105, 99, 104, 97, 114, 100,
32, - 72, 121, 100, 101
- for ( int i 0 i lt data.length i )
- out.writeInt(datai)
-
- out.flush()
- out.close()
- catch ( IOException x )
30Extra samples - pipes
- public static void readStuff(InputStream rawIn)
- try
- DataInputStream in new DataInputStream(
- new BufferedInputStream(rawIn))
- boolean eof false
- while ( !eof )
- try
- int i in.readInt()
- System.out.println("just read " i)
- catch ( EOFException eofx )
- eof true
-
-
- System.out.println("Read all data from the
pipe") - catch ( IOException x )
- x.printStackTrace()
-
31Extra samples - pipes
- public static void main(String args)
- try
- final PipedOutputStream out
- new PipedOutputStream()
- final PipedInputStream in
- new PipedInputStream(out)
- Runnable runA new Runnable()
- public void run()
- writeStuff(out)
-
- Thread threadA new Thread(runA, "threadA")
- threadA.start()
-
- Runnable runB new Runnable()
- public void run()
- readStuff(in)
-
- Thread threadB new Thread(runB, "threadB")
32The Singleton pattern
- Ensure that only one instance of a class is
created - Provide a global point of access to the object
- Examples
- Window managers
- Print spoolers
- Filesystems
33The Singletone pattern
34Singleton creation idiom
- class Singleton
- private static Singleton instance
- private Vector v
- private boolean inUse
- private Singleton()
- v new Vector()
- v.addElement(new Object())
- inUse true
-
- public static Singleton getInstance()
- if (instance null) //1
- instance new Singleton() //2
- return instance
//3 -
35This implementation is fine for a single-threaded
program
- Ensures that only one Singleton object is ever
created! - The constructor is declared private.
- The getInstance() method creates only one object.
36But.. for multi-threaded
- Thread 1 calls the getInstance() method and
determines that instance is null at //1. - Thread 1 enters the if block, but is preempted by
thread 2 before executing the line at //2. - Thread 2 calls the getInstance() method and
determines that instance is null at //1. - Thread 2 enters the if block and creates a new
Singleton object and assigns the variable
instance to this new object at //2. - Thread 2 returns the Singleton object reference
at //3. - Thread 2 is preempted by thread 1.
- Thread 1 starts where it left off and executes
line //2 which results in another Singleton
object being created. - Thread 1 returns this object at //3.
37Thread-safe getInstance() method
- public static synchronized Singleton
getInstance() -
- if (instance null) //1
- instance new Singleton() //2
- return instance //3
-
- This implementation is too expensive because you
pay the cost of synchronization for every
invocation of the method
38Wrapping line 2 with synch.
- public static Singleton getInstance()
-
- if (instance null)
-
- synchronized(Singleton.class)
- instance new Singleton()
-
-
- return instance
-
- We encounter the same problem..
39The problem..
- Two threads can get inside of the if statement
concurrently when instance is null - Note that when the second thread enters the
synchronized block, it does not check to see if
instance is non-null.
40Double-checked locking
- public static Singleton getInstance()
-
- if (instance null)
-
- synchronized(Singleton.class) //1
- if (instance null) //2
- instance new Singleton() //3
-
-
- return instance
-
41- Thread 1 enters the getInstance() method.
- Thread 1 enters the synchronized block at //1
because instance is null. - Thread 1 is preempted by thread 2.
- Thread 2 enters the getInstance() method.
- Thread 2 attempts to acquire the lock at //1
because instance is still null. However, because
thread 1 holds the lock, thread 2 blocks at //1. - Thread 2 is preempted by thread 1.
- Thread 1 executes and because instance is still
null at //2, creates a Singleton object and
assigns its reference to instance. - Thread 1 exits the synchronized block and returns
instance from the getInstance() method. - Thread 1 is preempted by thread 2.
- Thread 2 acquires the lock at //1 and checks to
see if instance is null. - Because instance is non-null, a second Singleton
object is not created and the one created by
thread 1 is returned.
42Out-of-order writes
- In line //3, the code creates a Singleton object
and initializes the variable instance to refer to
this object. - The problem with this line of code is that the
variable instance can become non-null before the
body of the Singleton constructor executes.
43Its happens.. JIT complilers
- Thread 1 enters the getInstance() method.
- Thread 1 enters the synchronized block at //1
because instance is null. - Thread 1 proceeds to //3 and makes instance
non-null, but before the constructor executes. - Thread 1 is preempted by thread 2.
- Thread 2 checks to see if instance is null.
Because it is not, thread 2 returns the instance
reference to a fully constructed, but partially
initialized, Singleton object. - Thread 2 is preempted by thread 1.
- Thread 1 completes the initialization of the
Singleton object by running its constructor and
returns a reference to it.
44 instance new Singleton()
- mem allocate() //Allocate
memory for Singleton object. - instance mem //Note that
instance is now non-null, but -
//has not been initialized. - ctorSingleton(instance) //Invoke
constructor for Singleton passing -
instance -
45The solution
- Accept the synchronization of a getInstance()
method - Forgo synchronization and use a static field.
46Solution
- class Singleton
-
- private Vector v
- private boolean inUse
- private static Singleton instance new
Singleton() - private Singleton()
-
- v new Vector()
- inUse true
- //...
-
- public static Singleton getInstance()
-
- return instance
-