...

CA670 - Concurrent Programming Processeses & Threads

by user

on
Category: Documents
30

views

Report

Comments

Transcript

CA670 - Concurrent Programming Processeses & Threads
Java Threads
Synchronisation
Multicore
CA670 - Concurrent Programming
Java Threads
David Sinclair
Java Threads
Synchronisation
Multicore
Processeses & Threads
Process A process is a self-contained computational unit with
its own execution path and local resources.
• Memory
• Communication ports
An application may be a single process or a set of
processes communicating with each other (possibly
on different physical machines.)
Thread A thread, also known as a lightweight process, is an
execution environment. Threads exist with a process
and all the threads in a process share the local
resources of the process, particularly local memory.
While this makes communications between tasks very
efficient via shared memory, it gives rise to several
issues that need to be handled carefully.
Java Threads
Synchronisation
Multicore
Threads in Java
Every application starts with one default thread, the main thread.
This thread, like any subsequently created thread, has the power to
create and kill threads.
Each thread created is an instance of the Thread class.
There are two approaches to creating a thread.
p u b l i c c l a s s H e l l o T h r e a d e x t e n d s Thread
{
p u b l i c void run ( )
{
System . o u t . p r i n t l n ( ” H e l l o World ! ” ) ;
}
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] )
{
( new H e l l o T h r e a d ( ) ) . s t a r t ( ) ;
}
}
Java Threads
Synchronisation
Multicore
Threads in Java (2)
While this is the easiest way, it is a bit restrictive in that your class
is a subclass of Thread. A more general way is to make your class
a subclass of Runnable. Thread is a subclass of Runnable, but
Runnable inherits other class that are useful for thread
management.
p u b l i c c l a s s H e l l o R u n n a b l e implements R u n n a b l e
{
p u b l i c void run ( )
{
System . o u t . p r i n t l n ( ” H e l l o a g a i n ! ” ) ;
}
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] )
{
( new Thread ( new H e l l o R u n n a b l e ( ) ) ) . s t a r t ( ) ;
}
}
We will use this method.
Java Threads
Synchronisation
Multicore
Threads in Java (3)
Each thread has a priority from 1 to 10. The JVM uses a
preemptive, priority-based scheduler that allocates the current time
slice as follows.
• The thread with the highest priority is selected.
• If two or more threads have the same priority, a first-in
first-out (FIFO) policy is followed.
• If the current thread is blocked or terminates, another thread
is scheduled.
• If a thread with a higher priority is unblocked or started, it
preempts the current thread.
A thread can voluntary give up control calling the yield or sleep
methods. This voluntary yielding of control is called Cooperative
Multitasking.
Java Threads
Synchronisation
Multicore
Yields, Sleeps & Interrupts
The yield method is a signal to the JVM that the thread is not
ding anything critical and is willing to let another thread run on
the JVM. The JVM is free to ignore the yield signal.
The sleep method suspends the current thread for the specified
time period and releases the JVM for another Runnable thread.
The interrupt method is a signal to a thread that it should
possibly take some new action. How a thread actually responds to
an interrupt is solely at the threads discretion. Usually it would
stop what it is currently doing.
There are 2 ways a thread can know if it has been interrupted.
• Catch an InterruptedException exception.
• Test the thread’s Interrupted flag by calling the interrupted
method.
Java Threads
Synchronisation
Multicore
Interrupted Examples
This example responds to an InterruptedException by stopping
it current activities.
try
{
Thread . s l e e p ( 2 0 0 0 ) ; // s l e e p f o r 2 s e c o n d s
} catch ( I n t e r r u p t e d E x c e p t i o n e )
{
return ;
}
This example test the Interrupted flag and throwing an
InterruptedException.
i f ( Thread . i n t e r r u p t e d ( ) )
{
throw new I n t e r r u p t e d E x c e p t i o n ( ) ;
}
Java Threads
Synchronisation
Multicore
Joins
When a thread invokes the join method on another thread, it will
wait for that thread to complete. I can be passed a parameter to
specify how long it should wait for the other thread to terminate
Like the sleep method, the join method also responds to an
interrupt signal with an InterruptedException.
Java Threads
Synchronisation
Multicore
A Simple Example
From Oracle’s Java Tutorials
p u b l i c c l a s s SimpleThreads
{
s t a t i c v o i d threadMessage ( S t r i n g message )
{
S t r i n g threadName = Thread . c u r r e n t T h r e a d ( ) . getName ( ) ;
System . o u t . f o r m a t ( ”%s : %s%n” , threadName , m e s s a g e ) ;
}
p r i v a t e s t a t i c c l a s s MessageLoop i m p l e m e n t s R u n n a b l e
{
p u b l i c void run ( )
{
S t r i n g i m p o r t a n t I n f o [ ] = {” Mares e a t o a t s ” , ” Does e a t o a t s ” ,
” L i t t l e l a m b s e a t i v y ” , ”A k i d w i l l e a t i v y t o o ” } ;
try
{
for ( int i = 0; i < importantInfo . length ;
{
Thread . s l e e p ( 4 0 0 0 ) ;
threadMessage ( importantInfo [ i ] ) ;
}
} catch ( I n t e r r u p t e d E x c e p t i o n e )
{
t h r e a d M e s s a g e ( ” I wasn ’ t done ! ” ) ;
}
i ++)
}
}
Java Threads
Synchronisation
A Simple Example (2)
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) t h r o w s I n t e r r u p t e d E x c e p t i o n
{
l o n g p a t i e n c e = 1000 ∗ 60 ∗ 6 0 ;
i f ( a r g s . l e n g t h > 0)
{
try
{
p a t i e n c e = Long . p a r s e L o n g ( a r g s [ 0 ] ) ∗ 1 0 0 0 ;
} catch ( NumberFormatException e )
{
System . e r r . p r i n t l n ( ” Argument must be an i n t e g e r . ” ) ;
System . e x i t ( 1 ) ;
}
}
t h r e a d M e s s a g e ( ” S t a r t i n g MessageLoop t h r e a d ” ) ;
l o n g s t a r t T i m e = System . c u r r e n t T i m e M i l l i s ( ) ;
Thread t = new Thread ( new MessageLoop ( ) ) ;
t . start ();
Multicore
Java Threads
Synchronisation
Multicore
A Simple Example (3)
t h r e a d M e s s a g e ( ” W a i t i n g f o r MessageLoop t h r e a d t o f i n i s h ” ) ;
while ( t . i s A l i v e ())
{
threadMessage ( ” S t i l l waiting . . . ” ) ;
t . join (1000);
i f ( ( ( System . c u r r e n t T i m e M i l l i s ( ) − s t a r t T i m e ) > p a t i e n c e ) && t . i s A l i v e ( ) )
{
threadMessage ( ” Tired of waiting ! ” ) ;
t . interrupt ();
t . join ();
}
}
threadMessage ( ” F i n a l l y ! ” ) ;
}
}
Java Threads
Synchronisation
When Threads Go Bad!
Consider the following program.
p u b l i c c l a s s TwoThreads {
p r i v a t e s t a t i c c l a s s RugbyLoop i m p l e m e n t s R u n n a b l e
{
p u b l i c void run ( )
{
try
{
f o r ( i n t i = 0 ; i < 1 0 0 ; i ++)
{
System . o u t . p r i n t ( ” I l i k e ” ) ;
Thread . s l e e p ( 1 0 ) ;
System . o u t . p r i n t l n ( ” Rugby ” ) ;
Thread . s l e e p ( 1 0 ) ;
}
} catch ( I n t e r r u p t e d E x c e p t i o n e )
{
System . o u t . p r i n t l n ( ” Rugby l o o p i n t e r r u p t e d ! ” ) ;
}
}
}
Multicore
Java Threads
Synchronisation
Multicore
When Threads Go Bad! (2)
p r i v a t e s t a t i c c l a s s B a l l e t L o o p implements Runnable
{
p u b l i c void run ( )
{
try
{
f o r ( i n t i = 0 ; i < 1 0 0 ; i ++)
{
System . o u t . p r i n t ( ” I h a t e ” ) ;
Thread . s l e e p ( 1 0 ) ;
System . o u t . p r i n t l n ( ” B a l l e t ” ) ;
Thread . s l e e p ( 1 0 ) ;
}
} catch ( I n t e r r u p t e d E x c e p t i o n e )
{
System . o u t . p r i n t l n ( ” B a l l e t l o o p i n t e r r u p t e d ! ” ) ;
}
}
}
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) t h r o w s I n t e r r u p t e d E x c e p t i o n
{
Thread r = new Thread ( new RugbyLoop ( ) ) ;
Thread b = new Thread ( new B a l l e t L o o p ( ) ) ;
r . start ();
b. start ();
r . join ();
b. join ();
}
}
Java Threads
Synchronisation
When Threads Go Bad! (3)
When this program is executed the output may not be what was
intended. The problem is due to contention on a shared resource,
2 threads trying to update a shared display.
While slightly amusing in this case, it can cause major systems
failure in other cases. Consider a banking application where 2
threads concurrently try to update your initial account balance of
e500 by e100 and e1000 respectively.
The relevant code in the 2 threads could be:
1.1 fload balance
2.1 fload balance
1.2 fadd 100
2.2 fadd 1000
1.3 fstore balance
2.3 fstore balance
The interleaving {1.1,1.2,1.3,2.1,2.2,2.3} will give the “right”
result, but the interleaving {2.1,2.2,1.1,2.3,1.2,1.3} will result in
e1000 missing from the balance.
Multicore
Java Threads
Synchronisation
Multicore
Critical Sections
The previous example shows us that some interleaving should
never be allowed. In general whenever a thread/process is updating
the state of a shared resource, no other thread/process should be
allowed to update the same shared resource while the first
thread/process is updating it. In this case the shared resource is
the shared variable balance.
These section of code that cannot be interleaved with related
sections of code are called critical sections.
If a thread/process is executing a critical section associated with a
shared variable balance, then it is OK if other threads are
executing:
• code of non-critical sections; or
• code of critical sections associated with other shared
resources.
Java Threads
Synchronisation
Multicore
Synchronization
Many different techniques (semaphores, monitors, rendezvous,
message passing) have been developed to ensure that 2 threads do
not execute associated critical sections at the same time. Java
uses a variation of the monitor concept called synchronization.
This comes in 2 forms:
• synchronized methods and;
• synchronized statements
Java Threads
Synchronisation
Multicore
Synchronized Methods
p u b l i c c l a s s BankAccount {
private int balance = 0;
...
p u b l i c s y n c h r o n i z e d v o i d d e p o s i t ( f l o a t amount )
{
b a l a n c e = b a l a n c e + amount ;
}
...
}
A synchronized method has 2 effects:
• 2 invocations of synchronized methods on the same object
cannot execute at the same time. When a thread is executing
a synchronized method of an object, then all other threads
that invoke any synchronized methods for the same object are
blocked and execution is suspended until the first thread is
done with the object.
Java Threads
Synchronisation
Multicore
Synchronized Methods (2)
• When a synchronized method exits, it creates a
happens-before relationship with any subsequent invocation of
any synchronized method of the same object. This guarantees
that changes to the state of the object are visible to all
threads.
Synchronization is built around an internal entity known as the
intrinsic lock or monitor lock. Every object has an intrinsic lock
(also known as a monitor lock) associated with it. A thread that
needs exclusive access to an object’s fields needs to acquire the
object’s intrinsic lock before accessing them, and then release the
intrinsic lock when it is finished. As long as a thread owns the
intrinsic lock, any other thread will block when it attempts to
acquire the lock.
Java Threads
Synchronisation
Multicore
Synchronized Statements
The following example synchronises on updating the member
variable balance but allow concurrent invocation of the
check owner method by many threads.
p u b l i c v o i d d e p o s i t ( S t r i n g name , f l o a t b a b l n c e )
{
i f ( c h e c k o w n e r ( name ) )
{
synchronized ( t h i s )
{
b a l a n c e = b a l a n c e + amount ;
}
}
}
Java Threads
Synchronisation
Multicore
Synchronized Statements (2)
Synchronized statements must specify the object that provides the
intrinsic lock, though it can be any object.
p u b l i c c l a s s MsLunch
{
p r i v a t e l o n g c1 = 0 , c2 = 0 ;
p r i v a t e O b j e c t l o c k 1 = new O b j e c t ( ) , l o c k 2 = new O b j e c t ( ) ;
public void inc1 ()
{
synchronized ( lock1 )
{
c1++;
}
}
public void inc2 ()
{
synchronized ( lock2 )
{
c2++;
}
}
}
Java Threads
Synchronisation
Multicore
Reentrant Synchronization
A thread cannot acquire a lock owned by another thread, But it
can acquire a lock that it already owns. Allowing a thread to
acquire the same lock more than once enables reentrant
synchronization. This allows a thread to directly or indirectly
invoke a method that also contains synchronized code, and both
the calling code and the called code use the same lock. Otherwise
without reentrant synchronization, we would have very complex
code to avoid having a thread causing itself to block.
Java Threads
Synchronisation
Multicore
Atomic Access
An atomic action is one that happens all at once. It cannot be
stopped in the middle. It either happens completely, or it doesn’t
happen at all.
The following are atomic actions.
• Reads from and writes to reference variables and for most
primitive variables (all types except long and double).
• Reads from and writes to all variables declared volatile
(including long and double variables).
Using simple atomic variable access is more efficient than accessing
these variables through synchronized code, but does define the
order in which the different threads perform the atomic accesses.
Java Threads
Synchronisation
Multicore
Deadlock
Deadlock occurs where two or more threads are blocked forever,
typically waiting for each other.
Consider the following problem called the Dining Philosophers
Problem. An institution hires five philosophers to solve a very
difficult problem. Each philosopher only engages in two activities thinking and eating. Meals are taken in the dining room which has
a table set with five plates and five forks. In the centre of the table
is a bowl of spaghetti that is endlessly replenished. The
philosophers, not being the most dextrous of individuals, requires
two forks to eat; and may only pick up the forks immediately to his
left and right.
Java Threads
Synchronisation
Deadlock (2)
For this system to operate correctly it is required that:
• A philosopher eats only if he has two forks.
• No two philosophers can hold the same fork simultaneously.
• No deadlock.
• No individual starvation.
• Efficient behaviour under the absence of contention.
This problem is a generalisation of multiple processes accessing a
set of shared resources; e.g. a network of computers accessing an
array of storage disks.
Multicore
Java Threads
Synchronisation
Multicore
Deadlock (3)
We can model exclusive access to each fork by using synchronized
statements.
c l a s s P h i l o s o p h e r implements Runnable
{
public f i n a l int id ;
private f i n a l Chopstick [ ] chopsticks ;
protected f i n a l ChopstickOrder order ;
p u b l i c P h i l o s o p h e r ( i n t id , Chopstick [ ] chopsticks ,
ChopstickOrder order )
{
this . id = id ;
this . chopsticks = chopsticks ;
this . order = order ;
}
p u b l i c void run ( )
{
while ( true )
{
eat ( ) ;
}
}
Java Threads
Synchronisation
Multicore
Deadlock (4)
protected void eat ()
{
Chopstick [ ] chopstickOrder = order . getOrder ( getLeft () ,
getRight ( ) ) ;
synchronized ( chopstickOrder [ 0 ] )
{
synchronized ( chopstickOrder [ 1 ] )
{
Util . sleep (1000);
}
}
}
Chopstick getLeft ()
{
return chopsticks [ id ] ;
}
Chopstick getRight ()
{
r e t u r n c h o p s t i c k s [ ( i d +1) % c h o p s t i c k s . l e n g t h ] ;
}
}
Java Threads
Synchronisation
Multicore
Deadlock (5)
This is called a symmetric solution since each task is identical.
Symmetric solutions have many advantages, particularly when it
comes to load-balancing.
However, this system can deadlock under an interleaving in which
all five philosophers pick up their left forks together.
There are several solutions.
• Make one of the philosophers pick up the right fork before the
left fork (asymmetric solution).
• Make a philosopher release their left fork if they cannot secure
their right fork.
• Only allow four philosophers into the room at any one time.
Java Threads
Synchronisation
Multicore
Lock Objects
In order to provide a more sophisticate locking mechanism than
synchronized code, Java provides the
java.util.concurrent.locks package. In particular we will
focus on its most basic interface, Lock.
Lock objects work very much like the implicit locks used by
synchronized code. As with implicit locks, only one thread can own
a Lock object at a time. Lock objects provide a more sophisticated
implementation of monitors by providing a wait/notify mechanism,
through their associated Condition objects.
The main advantage of Lock objects over implicit locks is their
ability to back out of an attempt to acquire a lock. The tryLock
method backs out if the lock is not available immediately or before
a specified timeout has expired. The lockInterruptibly method
backs out if another thread sends an interrupt before the lock is
acquired.
Java Threads
Synchronisation
Multicore
Lock Objects (2)
p u b l i c c l a s s GraciousPhilosopher extends Philosopher
{
p r i v a t e s t a t i c Map c h o p s t i c k L o c k s = new ConcurrentHashMap ( ) ;
p u b l i c G r a c i o u s P h i l o s o p h e r ( i n t id , Chopstick [ ] chopsticks ,
ChopstickOrder order )
{
super ( id , chopsticks , order ) ;
// E v e r y p h i l o s o p h e r c r e a t e s a l o c k
// f o r t h e i r l e f t c h o p s t i c k
c h o p s t i c k L o c k s . p u t ( g e t L e f t ( ) , new R e e n t r a n t L o c k ( ) ) ;
}
protected void eat ()
{
Chopstick [ ] chopstickOrder = order . getOrder ( getLeft () ,
getRight ( ) ) ;
Lock f i r s t L o c k = c h o p s t i c k L o c k s . g e t ( c h o p s t i c k O r d e r [ 0 ] ) ;
Lock s e c o n d L o c k = c h o p s t i c k L o c k s . g e t ( c h o p s t i c k O r d e r [ 1 ] ) ;
Java Threads
Synchronisation
Lock Objects (3)
firstLock . lock ();
try
{
secondLock . l o c k ( ) ;
try
{
sleep (1000);
} finally
{
secondLock . unlock ( ) ;
}
} finally
{
f i r s t L o c k . unlock ( ) ;
}
}
}
Multicore
Java Threads
Synchronisation
Multicore
Lock Objects (4)
Or using the tryLock method
p u b l i c c l a s s GraciousPhilosopher extends Philosopher
{
p r i v a t e s t a t i c Map c h o p s t i c k L o c k s = new ConcurrentHashMap ( ) ;
p u b l i c G r a c i o u s P h i l o s o p h e r ( i n t id , Chopstick [ ] chopsticks ,
ChopstickOrder order )
{
super ( id , chopsticks , order ) ;
// E v e r y p h i l o s o p h e r c r e a t e s a l o c k
// f o r t h e i r l e f t c h o p s t i c k
c h o p s t i c k L o c k s . p u t ( g e t L e f t ( ) , new R e e n t r a n t L o c k ( ) ) ;
}
protected void eat ()
{
Chopstick [ ] chopstickOrder = order . getOrder ( getLeft () ,
getRight ( ) ) ;
Lock f i r s t L o c k = c h o p s t i c k L o c k s . g e t ( c h o p s t i c k O r d e r [ 0 ] ) ;
Lock s e c o n d L o c k = c h o p s t i c k L o c k s . g e t ( c h o p s t i c k O r d e r [ 1 ] ) ;
Java Threads
Synchronisation
Multicore
Lock Objects (5)
Boolean l e f t L o c k = f a l s e , r i g h t L o c k = f a l s e ;
try
{
leftLock = f i r s t L o c k . tryLock ( ) ;
r i g h t L o c k = secondLock . tryLock ( ) ;
} finally
{
i f ( l e f t L o c k && r i g h t L o c k )
{
sleep (1000);
// e a t
}
i f ( leftLock ) f i r s t L o c k . unlock ( ) ;
i f ( r i g h t L o c k ) secondLock . unlock ( ) ;
}
}
}
We still have some issues with this solution. Though there is no
deadlock, there is the possibility of individual threads starving and
fairness to the access of the shared resource.
Java Threads
Synchronisation
Multicore
wait() & notify()
synchronized methods and blocks acquire and release a lock
within a method. To have one methods acquire a lock and another
release it, you need to use the wait-notify mechanism.
• wait() tells the calling thread to give up the monitor and go
to sleep until some other thread enters the same monitor and
calls notify( ).
• notify() wakes up the first thread that called wait() on the
same object.
• notifyAll() wakes up all threads blocked on the same
object.
Java Threads
Synchronisation
Multicore
wait() & notify() (2)
p u b l i c c l a s s ThreadA
{
p u b l i c s t a t i c v o i d main ( S t r i n g [ ]
{
ThreadB b = new ThreadB ( ) ;
b. start ();
args )
synchronized (b)
{
try
{
System . o u t . p r i n t l n ( ” W a i t i n g f o r b t o c o m p l e t e . . ” ) ;
b . wait ( ) ;
} catch ( I n t e r r u p t e d E x c e p t i o n e )
{
e . printStackTrace ( ) ;
}
System . o u t . p r i n t l n ( ” T o t a l i s : ” + b . t o t a l ) ;
}
}
}
Java Threads
Synchronisation
Multicore
wait() & notify() (3)
c l a s s ThreadB e x t e n d s Thread
{
int total ;
@Override
p u b l i c void run ( )
{
synchronized ( this )
{
f o r ( i n t i =0; i <100 ;
{
t o t a l += i ;
}
notify ();
}
}
i ++)
}
Java Threads
Synchronisation
Multicore
Condition Variables
Condition variables factor out and Object’s wait, notify and
notifyAll methods into distinct objects to give the effect of
having multiple wait-sets per object.
A Condition instance is intrinsically bound to a lock. To obtain a
Condition instance for a particular Lock instance use its
newCondition() method.
A common paradigm for concurrent programming is the
Producer-Consumer paradigm in which Producers generates work
for multiple Consumers to process. The Producers are connected
to the Consumers via a bounded buffer.
Java Threads
Synchronisation
Multicore
Producer-Consumer
c l a s s BoundedBuffer
{
f i n a l Lock l o c k = new R e e n t r a n t L o c k ( ) ;
f i n a l Condition n o t F u l l = lock . newCondition ( ) ;
f i n a l C o n d i t i o n notEmpty = l o c k . n e w C o n d i t i o n ( ) ;
f i n a l O b j e c t [ ] i t e m s = new O b j e c t [ 1 0 0 ] ;
i n t putptr , takeptr , count ;
p u b l i c void put ( Object x ) throws I n t e r r u p t e d E x c e p t i o n
{
lock . lock ();
try
{
w h i l e ( c o u n t == i t e m s . l e n g t h )
{
notFull . await ( ) ;
}
items [ putptr ] = x ;
i f (++ p u t p t r == i t e m s . l e n g t h )
{
putptr = 0;
}
++c o u n t ;
notEmpty . s i g n a l ( ) ;
} finally
{
lock . unlock ( ) ;
}
}
Java Threads
Synchronisation
Producer-Consumer (2)
p u b l i c Object take ( ) throws I n t e r r u p t e d E x c e p t i o n
{
lock . lock ();
try
{
w h i l e ( c o u n t == 0 )
{
notEmpty . a w a i t ( ) ;
}
Object x = items [ takeptr ] ;
i f (++ t a k e p t r == i t e m s . l e n g t h )
{
takeptr = 0;
}
−−c o u n t ;
notFull . signal ();
return x ;
} finally
{
lock . unlock ( ) ;
}
}
}
Multicore
Java Threads
Synchronisation
Multicore
Running Threads on Different Cores
We have seen how to create multiple threads and coordinate them
via synchronized methods and blocks, as well as via Lock
objects. But how do we execute the threads to different cores on a
multicore machine?
There are 2 mechanisms in Java
• Executors Interface and Thread Pools
• Fork/Join Framework
Java Threads
Synchronisation
Multicore
Executor Interface & Thread Pools
The java.util.concurrentpackage provides 3 executor
interfaces.
• Executor: A simple interface that launches new tasks.
• ExecutorService: A subinterface of Executor that adds
features that help manage tasks’ lifecycle.
• ScheduledExecutorService: A subinterface of
ExecutorService that supports future and/or periodic
execution of tasks.
The Executor interface provides a single method, execute. If r is
a Runnable object, and e is an Executor object then
e . execute ( r ) ;
may simply execute a thread, or it may use an existing worker
thread to run r, or using thread pools it may place r in a queue to
wait for a worker thread to become available.
Java Threads
Synchronisation
Multicore
Executor Example
p u b l i c c l a s s WorkerThread i m p l e m e n t s R u n n a b l e
{
p r i v a t e S t r i n g command ;
p u b l i c WorkerThread ( S t r i n g s )
{
t h i s . command=s ;
}
@Override
p u b l i c void run ( )
{
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ()+
” S t a r t . Command = ”+command ) ;
processCommand ( ) ;
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ()+
” End . ” ) ;
}
Java Threads
Synchronisation
Executor Example (2)
p r i v a t e v o i d processCommand ( )
{
try
{
Thread . s l e e p ( 5 0 0 0 ) ;
} catch ( I n t e r r u p t e d E x c e p t i o n e )
{
e . printStackTrace ( ) ;
}
}
@Override
public String toString ()
{
r e t u r n t h i s . command ;
}
}
Multicore
Java Threads
Synchronisation
Multicore
Executor Example (3)
import j a v a . u t i l . c o n c u r r e n t . E x e c u t o r S e r v i c e ;
import j a v a . u t i l . c o n c u r r e n t . Executors ;
p u b l i c c l a s s SimpleThreadPool
{
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s )
{
ExecutorService executor =
Executors . newFixedThreadPool ( 1 0 ) ;
f o r ( i n t i = 0 ; i < 2 0 ; i ++)
{
R u n n a b l e w o r k e r = new WorkerThread ( ” ” + i ) ;
executor . execute ( worker ) ;
}
e x e c u t o r . shutdown ( ) ;
while (! executor . isTerminated ())
{
}
System . o u t . p r i n t l n ( ” F i n i s h e d
all
threads ” );
}
}
Java Threads
Synchronisation
Multicore
Fork/Join Framework
Since Java 7, the Fork/Join framework has been available to
distribute threads among multiple cores. This framework adopts a
divide-and-conquer approach. If a task can be easily solved the
current thread returns its result. Otherwise the thread divides the
task into simpler tasks and forks a thread for each sub-task. When
all the sub-tasks are completed, the current thread returns its
result obtained from combining the results of its sub-tasks.
The key difference between the Fork/Join framework and Executor
Interface is that the Fork/Join framework implements a work
stealing algorithm whereby idle threads steal work from threads
that are still busy.
Java Threads
Synchronisation
Multicore
Fork/Join Classes
A key class is the ForkJoinPool which is an implementation of
the ExecutorService that implements the work-stealing
algorithm. A ForkJoinPool is instantiated as follows.
numberOfCores = Runtime . getRunTIme ( ) . a v a i l a b l e P r o c e s s o r s ( ) ;
F o r k J o i n P o o l p o o l = new F o r k J o i n P o o l ( numberOfCores ) ;
The size of the pool at any point in time will be adjusted
automatically to maintain enough active threads. Unlike the
ExecutorService the ForJoinPool does not need to be
explicitly shutdown.
There are 3 ways to submit tasks to a ForkJoinPool
• execute(): asynchronous execution
• invoke(): synchronous execution - wait for the result
• invoke(): asynchronous execution - returns a Future object
that can be used to check the status of the execution and
obtain the results.
Java Threads
Synchronisation
Multicore
Fork/Join Classes (2)
The ForkJoinTask class is an abstract class that is used to create
tasks to be executed from the ForkJoinPool. There are 2
subclasses:
• RecursiveTask: returns an object of a specified type.
• RecursiveAction: has no return object.
The status of a task can be checked using:
• isDone(): returns true if the tasks has finished execution.
• isCompletedNormally(): returns true if the task has
completed without cancellation or exception.
• isCancelled(): returns true if the task has been cancelled.
• isCompletedAbnormally(): returns true if the task has
been terminated by an exception.
Java Threads
Synchronisation
Multicore
Fork/Join Example
i m p o r t j a v a . u t i l . Random ;
import j a v a . u t i l . c o n c u r r e n t . ForkJoinPool ;
import j a v a . u t i l . c o n c u r r e n t . RecursiveTask ;
p u b l i c c l a s s MaximumFinder e x t e n d s R e c u r s i v e T a s k <I n t e g e r >
{
p r i v a t e s t a t i c f i n a l i n t SEQUENTIAL THRESHOLD = 5 ;
private
private
private
final
final
final
i n t [ ] data ;
int start ;
i n t end ;
p u b l i c MaximumFinder ( i n t [ ] d at a , i n t s t a r t , i n t end )
{
t h i s . data = data ;
this . start = start ;
t h i s . end = end ;
}
p u b l i c MaximumFinder ( i n t [ ] d a t a )
{
t h i s ( da ta , 0 , d a t a . l e n g t h ) ;
}
Java Threads
Synchronisation
Multicore
Fork/Join Example (2)
@Override
p r o t e c t e d I n t e g e r compute ( )
{
f i n a l i n t l e n g t h = end − s t a r t ;
i f ( l e n g t h < SEQUENTIAL THRESHOLD)
{
return computeDirectly ( ) ;
}
f i n a l int s p l i t = length / 2;
f i n a l MaximumFinder l e f t = new MaximumFinder ( dat a , s t a r t ,
start + split );
l e f t . fork ();
f i n a l MaximumFinder r i g h t = new MaximumFinder ( dat a ,
s t a r t + s p l i t , end ) ;
r e t u r n Math . max ( r i g h t . compute ( ) , l e f t . j o i n ( ) ) ;
}
Java Threads
Synchronisation
Multicore
Fork/Join Example (3)
private Integer computeDirectly ()
{
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) + ” c o m p u t i n g : ”
+ s t a r t + ” t o ” + end ) ;
i n t max = I n t e g e r . MIN VALUE ;
f o r ( i n t i = s t a r t ; i < end ; i ++)
{
i f ( d a t a [ i ] > max )
{
max = d a t a [ i ] ;
}
}
r e t u r n max ;
}
Java Threads
Synchronisation
Multicore
Fork/Join Example (4)
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s )
{
// c r e a t e a random d a t a s e t
f i n a l i n t [ ] d a t a = new i n t [ 1 0 0 0 0 ] ;
f i n a l Random random = new Random ( ) ;
f o r ( i n t i = 0 ; i < d a t a . l e n g t h ; i ++)
{
d a t a [ i ] = random . n e x t I n t ( 1 0 0 ) ;
}
// s u b m i t t h e t a s k t o t h e p o o l
f i n a l F o r k J o i n P o o l p o o l = new F o r k J o i n P o o l ( 4 ) ;
f i n a l MaximumFinder f i n d e r = new MaximumFinder ( d a t a ) ;
System . o u t . p r i n t l n ( p o o l . i n v o k e ( f i n d e r ) ) ;
}
}
Fly UP