0% found this document useful (0 votes)
62 views19 pages

PP Unit V

The document discusses concurrent programming and threads. It defines multitasking as a computer's ability to perform multiple jobs concurrently by running multiple programs at the same time. Multithreading refers to multiple threads of control within a single program. A thread is a single sequence of execution within a program. Threads enable a program to run multiple parts of itself at the same time. Java supports creating threads by either extending the Thread class or implementing the Runnable interface.

Uploaded by

Arul Jothi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
62 views19 pages

PP Unit V

The document discusses concurrent programming and threads. It defines multitasking as a computer's ability to perform multiple jobs concurrently by running multiple programs at the same time. Multithreading refers to multiple threads of control within a single program. A thread is a single sequence of execution within a program. Threads enable a program to run multiple parts of itself at the same time. Java supports creating threads by either extending the Thread class or implementing the Runnable interface.

Uploaded by

Arul Jothi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 19

UNIT V CONCURRENT PROGRAMMING

What you want:


The computer that runs multiple programs at the same time.
A program with seperate independently running subtasks.
What you usually have:
A single CPU which can run one program at a time.
Software which makes it appear that multiple programs are running ,by giving them
each part of the CPUs time.
Multitasking and Multithreading:
Multitasking refers to a computer's ability to perform multiple jobs concurrently
- more than one program are running concurrently, e.g., UNIX
A thread is a single sequence of execution within a program
Multithreading refers to multiple threads of control within a single program.
Each program can run multiple threads of control within it, e.g., Web Browser
Current Terminology:
Process/Task:
A process (or task) is a self-contained instance of a program, running in its own
address space.
A multi-tasking operating system(e.g.Unix) runs multiple tasks(processes) at a
Time.Each thinks it has the CPU to itself.
Preemptive multitasking means the OS can switch tasks at any time.
Cooperative multitasking means the OS must wait for each task to give up
the CPU.
Threads:
A thread is a single sequential flow of control within one process.
Lighter weight than a separate process.
Memory is shared among threads.
Typically tie threads to events, e.g.
Button push
Timer
Menu choice
Java supports threads.
What are Threads Good For?
To maintain responsiveness of an application during a long running task.
To enable cancellation of separable tasks.

Application Thread:
When we execute an application:
The JVM creates a Thread object whose task is defined by the main() method
It starts the thread
The thread executes the statements of the program one by one until the
method returns and the thread dies
Multiple Threads in an Application:
Each thread has its private run-time stack
If two threads execute the same method, each will have its own copy of the local variables the
methods uses
However, all threads see the same dynamic memory (heap)
Two different threads can act on the same object and same static fields concurrently
Java Threads:
Implementation is up to the JVM:
it's not defined whether a Java thread is mapped to a something else at the OS
level (a native thread or process), or whether the JVM does timesharing itself.
Every Java program is a threaded program!
garbage collection runs as a thread.

Threads
Threads enable a single program to run multiple parts of itself at the same time. For
example, one part of a program can display an animation on the screen while another part
builds the next animation to be displayed.
Threads are a lightweight version of a process. Threads are similar to processes in that
they can be executed independently and simultaneously, but are different in that they do not have
all the overhead that a process does.
Threads do not make copies of the entire parent process. Instead, only the code needed is
run in parallel. This means that threads can be started quickly because they don't have all of the
overhead of a complete process. They do, however, have complete access to all data of the parent
process.
Threads can read and/or write data that any other thread can access. This makes
interthread communication simpler, but can lead to multiple threads modifying data in an
unpredictable manner.

Uses of Threads
Threads are useful programming tools for two main reasons.
They enable programs to do multiple things at one time. This is useful for such

activities as letting a user do something while something else happens in the background.
Threads enable the programmer to concentrate on program functionality without
worrying about the implementation of multitasking schemes. Declaring Threads
Creating Threads:
There are two ways to create our own Thread object.
Subclassing the Thread class and instantiating a new object of that class
Implementing the Runnable interface.
In both cases the run() method should be implemented.
To Create a thread:
Instantiate an object with new on the class you defined.
Invoke start() on the thread object.
This initializes the thread and invokes its run() method.
Could exit using the return command.
Could be an infinite loop.
Will run until the entire program stops.
Java will occasionally switch to another thread.
Should use a sleep() or something to keep it from hogging the CPU.
Can still be preempted without sleep(),but the whole system will be less
responsive.
Creating a thread:
Create a java.lang.Thread object.
creates a new thread, but doesn't tell the thread what to do.
In other languages we tell a new thread what method to run - in Java we can't do this (no
pointers to methods!).
In Java we need to tell the new thread what Object to run - the Object
must implement the Runnable interface.
Extending Thread:
public class ThreadExample extends Thread {
public void run () {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread: + i);
}
}
}

Thread Properties:
void start()
Creates a new thread and makes it runnable

This method can be called only once


void run()
The new thread begins its life inside this method
void stop() (deprecated)
The thread is being terminated
yield()
Causes the currently executing thread object to temporarily pause and allow
other threads to execute
Allow only threads of the same priority to run
sleep(int m)/sleep(int m,int n)
The thread sleeps for m milliseconds, plus n nanoseconds
The Runnable interface:
public interface Runnable {
abstract public void run();
}
A class that implements Runnable must have a run() method.
this is the code that will be run by the thread.
Implementing Runnable:
public class RunnableExample implements Runnable {
public void run () {

323
for (int i = 1; i <= 100; i++) {
System.out.println (Runnable: + i);
}
}
}

A Runnable Object:
The Thread objects run() method calls the Runnable objects run() method
Allows threads to run inside any object, regardless of inheritance

Thread State Diagram:

Thread Lifetime:
A thread is done when:
the run() method returns.
uncaught exception occurs.
someone calls the threads stop() method (which is deprecated and shouldn't be used!).
A program is done when all (non-daemon) threads have finished.

Example:
public class PrintThread1 extends Thread {
String name;
public PrintThread1(String name) {
this.name = name;
}
public void run() {
for (int i=1; i<500 ; i++) {
try {
sleep((long)(Math.random() * 100));
} catch (InterruptedException ie) { }
System.out.print(name);
}}
public static void main(String args[]) {
PrintThread1 a = new PrintThread1("*");
PrintThread1 b = new PrintThread1("-");
PrintThread1 c = new PrintThread1("=");
a.start();
b.start();
c.start();
Alive
New Thread Dead Thread
Running
Runnable
new ThreadExample();
run() method returns
while () { }
Blocked
Object.wait()
Thread.sleep()
blocking IO call
waiting on a monitor
thread.start();
}
}

INTERRUPTING THREADS
To create classes that make use of threads, you can extend the class Thread or implement
the interface Runnable. Both yield the same result. By implementing Runnable, existing classes
can be converted to threads without having to change the classes on which they are based.

Creating Threads by Extending Thread


An example of creating a thread by extending class Threadfollows:

publicclassMyMain{
publicstaticvoidmain(Stringargs[]){
CntThreadcntThread;//declarethread
cntThread=newCntThread();//createthread
cntThread.start();//startthreadrunning
try{System.in.read();}//waitforkeyboardinput
catch(java.io.IOExceptione){}
cntThread.stop();//stopthread
}
}
classCntThreadextendsThread{
publicvoidrun(){
intix=0;
while(true){
System.out.println("running,ix="+ix++);//writecountto
screen
try{Thread.sleep(1000);}//sleep1second
catch(InterruptedExceptione){}
}
}
}

In this example, a thread is created that will write an incrementing counter to the screen. It will
continue to count until the mainroutine receives a character from the keyboard, at which time
the counting thread stops. This means you can press any key on the keyboard followed by the
Enter key or press only the Enter key to stop the thread from counting.

Creating Threads by Implementing Runnable


The second way to create a thread is by implementing the Runnable interface. The
following code creates a thread that increments a counter until a character is entered from the
keyboard:
import java.applet.*;
import java.awt.*;
public class MyApplet extends Applet implements Runnable {
int ix = 0;
Thread mainThread;
CntThread cntThread;
public void start() {
if (mainThread == null) {
mainThread = new Thread(this);
mainThread.start(); //start main thread
}
}
public void run() {
cntThread = new CntThread(this); //create CntThread instance
cntThread.start(); //start cntThread instance
}
public boolean keyDown(Event evt, int key) { //process key press

cntThread.stop(); //stop cntThread instance


return(true);
}
public void paint(Graphics g) {
g.drawString("running, ix = " + ix, 10,20); //write count to screen
}
}
class CntThread implements Runnable {
MyApplet parent;
boolean loop;
Thread cntThread;
public CntThread(MyApplet p) { //constructor for CntThread
parent = p; //save parent instance
}
public void start() {
if (cntThread == null) {
cntThread = new Thread(this); //create counting thread
cntThread.start(); //start counting thread
}
}
public void stop() {
loop = false; //set value to exit main while loop
}
public void run() {

327
loop = true;
while (loop == true) {
parent.ix++; //incremen t counter
parent.repaint(); //repaint screen
try {Thread.sleep(1000);} //sleep 1 second
catch(InterruptedException e) {}
}
}
}

This example also uses tryand catchfor exceptions.


new and the Instantiation of Threads

Instances of threads are created using the standard newkeyword. Arguments to newcan
either use the Threadclass explicitly, as in
mainThread=newThread(this);

or specify a class that is a subclass of Thread, like this:


mainThread=newMyThreadClass();

In the first example, the current instance, this, of an object is initialized as a thread. Any
object, however, can be passed as an argument to the Threadclass.
Note that the Thread class has few constructors. If additional constructor types are
needed, creating a subclass of Threadwith the needed constructors is quite useful. The second
thread creation example allows for these possibilities.

Destroying a Thread
The execution of a thread can be controlled in several ways using the stop, start, and
destroy methods. The object remains in existence as long as the object is referenced somewhere,
even if stopis invoked.
It is not necessary to explicitly destroy the thread object. Java's garbage collector takes care
of this detail. If it is necessary to give the garbage-collection process a helping hand, make sure
all references are removed to the thread object.
The simplest way to do this is to assign the value nullto all variables containing thread
references, as in the following example:
Thread myThread = new Thread();
myThread.start();
myThread.stop();
myThread = null;

In this example, the thread object is instantiated with new. The thread is then started and
stopped. Finally, null is assigned to the variable containing the thread instance. This last step
ensures that Java's garbage collector will schedule a resource deallocation for that object.

Named Threads
Java provides a means for assigning names to threads. Names can consist of any valid Java
string. Naming threads makes it convenient to distinguish one thread from another and enables
a parent process to query a thread for its name. A thread can be assigned a name at creation
time or at any point thereafter. Threads can also be renamed at any time.
To assign a thread a name at creation time, simply use a Threadconstructor that accepts a
string as an additional argument, like this:
Thread myThread = new Thread(this."My first named thread");

In this example, the thread is instantiated by newand assigned a name of "Myfirstnamed


thread".

The getName Method


A thread name can be obtained using the getName method. getName returns the name
associated with a specified thread object, as in the following example:
System.out.println("The name of this thread is " + myThread.getName());

This example prints out the name assigned to the thread object myThread. Using the previous
example, this statement would print the following to the screen:
ThenameofthisthreadisMyfirstnamedthread

A thread can also query for its own name in the same way:
System.out.println("Mynameis"+this.getName());

The setName Method


You can set or change a thread name after creation using the setName method. The
parent
process, the thread itself, or any other method that has access to the thread object can do this.

Following is an example of changing a thread name using setName:


myThread.setName("My newly renamed first thread");

This example changes the name of the thread from "Myfirstnamedthread"to "Mynewly
renamedfirstthread".

Synchronization of Threads
Multiple threads can access the same object or method because threads are not
independent processes with complete copies of data objects. However, there is no guarantee which
thread will access an object at a given time, which can lead to unpredictable results. In situations
when this is not acceptable, use the Java synchronization logic. The keyword that provides this
logic is synchronized.

Synchronization
Threads communicate primarily by sharing access to fields and the objects reference fields
refer to. This form of communication is extremely efficient, but makes two kinds of errors
possible: thread interference and memory consistency errors. The tool needed to prevent these
errors is synchronization.
Thread Interference describes how errors are introduced when multiple threads access
shared data.
Memory Consistency Errors describes errors that result from inconsistent views of
shared memory.
Synchronized Methods describes a simple idiom that can effectively prevent thread
interference and memory consistency errors.
Implicit Locks and Synchronization describes a more general synchronization idiom,
and describes how synchronization is based on implicit locks.
Atomic Access talks about the general idea of operations that can't be interfered with by
other threads.

Thread Interference
Consider a simple class called Counter
classCounter{
privateintc=0;
publicvoidincrement(){
c++;
}
publicvoiddecrement(){
c;
}
publicintvalue(){
returnc;
}
}

Counteris designed so that each invocation of incrementwill add 1 to c, and each invocation

of decrementwill subtract 1 from c. However, if a Counterobject is referenced from multiple


threads, interference between threads may prevent this from happening as expected.
Interference happens when two operations, running in different threads, but acting on the same
data, interleave. This means that the two operations consist of multiple steps, and the
sequences of steps overlap.
It might not seem possible for operations on instances of Counterto interleave, since both
operations on care single, simple statements. However, even simple statements can translate to
multiple steps by the virtual machine. We won't examine the specific steps the virtual machine
takes it is enough to know that the single expression c++can be decomposed into three
steps:
1. Retrieve the current value of c.
2. Increment the retrieved value by 1.
3. Store the incremented value back in c.
The expression ccan be decomposed the same way, except that the second step decrements
instead of increments.
Suppose Thread A invokes incrementat about the same time Thread B invokes decrement. If
the initial value of cis 0, their interleaved actions might follow this sequence:
1. Thread A: Retrieve c.
2. Thread B: Retrieve c.
3. Thread A: Increment retrieved value; result is 1.
4. Thread B: Decrement retrieved value; result is -1.
5. Thread A: Store result in c; c is now 1.
6. Thread B: Store result in c; c is now -1.
Thread A's result is lost, overwritten by Thread B. This particular interleaving is only one
possibility. Under different circumstances it might be Thread B's result that gets lost, or there
could be no error at all. Because they are unpredictable, thread interference bugs can be
difficult to detect and fix.

Memory Consistency Errors


Memory consistency errors occur when different threads have inconsistent views of what
should be the same data. The causes of memory consistency errors are complex and beyond the
scope of this tutorial. Fortunately, the programmer does not need a detailed understanding of
these causes. All that is needed is a strategy for avoiding them.
The key to avoiding memory consistency errors is understanding the happens-before
relationship. This relationship is simply a guarantee that memory writes by one specific
statement are visible to another specific statement. To see this, consider the following example.
Suppose a simple intfield is defined and initialized:
intcounter=0;

The counterfield is shared between two threads, A and B. Suppose thread A increments
counter:

counter++;

Then, shortly afterwards, thread B prints out counter:


System.out.println(counter);

If the two statements had been executed in the same thread, it would be safe to assume that the
value printed out would be "1". But if the two statements are executed in separate threads, the
value printed out might well be "0", because there's no guarantee that thread A's change to
counterwill be visible to thread B unless the programmer has established a happens-before
relationship between these two statements.
There are several actions that create happens-before relationships. One of them is synchronization,
as we will see in the following sections.
We've already seen two actions that create happens-before relationships.
When a statement invokes Thread.start, every statement that has a happens-before
relationship with that statement also has a happens-before relationship with every
statement executed by the new thread. The effects of the code that led up to the creation
of the new thread are visible to the new thread.
When a thread terminates and causes a Thread.joinin another thread to return, then
all the statements executed by the terminated thread have a happens-before relationship
with all the statements following the successful join. The effects of the code in the
thread are now visible to the thread that performed the join.
For a list of actions that create happens-before relationships, refer to the Summary page of the
java.util.concurrentpackage..

The synchronized Keyword


The synchronized keyword is used to lock an object long enough to execute a block of
code.
No other thread can make changes to the specified object while the block of code is being
executed.
Here is an example using synchronized:
public void printIndex() {

syncronized(index) {
index++;
System.out.println("index = " + index);
}
}

In this example, the printIndexmethod contains a synchronized block of code. In this


block, the object index is locked while the block of code making up the body of the
synchronizedstatement is executed. The increment of indexand the printing of the value of
indexare contained inside the block. This ensures that the value of indexis printed to the screen
without having to worry that some other thread incremented indexbefore it was printed.

Synchronized Methods

The Java programming language provides two basic synchronization idioms:


synchronized
methods and synchronized statements. The more complex of the two, synchronized statements,
are described in the next section. This section is about synchronized methods.
To make a method synchronized, simply add the synchronizedkeyword to its declaration:
publicclassSynchronizedCounter{
privateintc=0;
publicsynchronizedvoidincrement(){
c++;
}
publicsynchronizedvoiddecrement(){
c;
}
publicsynchronizedintvalue(){
returnc;
}
}

If countis an instance of SynchronizedCounter, then making these methods synchronized


has two effects:
First, it is not possible for two invocations of synchronized methods on the same object
to interleave. When one thread is executing a synchronized method for an object, all
other threads that invoke synchronized methods for the same object block (suspend
execution) until the first thread is done with the object.
Second, when a synchronized method exits, it automatically establishes a happensbefore
relationship with any subsequent invocation of a synchronized method for the
same object. This guarantees that changes to the state of the object are visible to all
threads.
Note that constructors cannot be synchronized using the synchronizedkeyword with a
constructor is a syntax error. Synchronizing constructors doesn't make sense, because only the
thread that creates an object should have access to it while it is being constructed.
Warning: When constructing an object that will be shared between threads, be very careful
that a reference to the object does not "leak" prematurely. For example, suppose you want to
333
maintain a Listcalled instancescontaining every instance of class. You might be tempted to
add the line
instances.add(this);

to your constructor. But then other threads can use instancesto access the object before
construction of the object is complete.
Synchronized methods enable a simple strategy for preventing thread interference and memory
consistency errors: if an object is visible to more than one thread, all reads or writes to that
object's variables are done through synchronizedmethods. (An important exception: final
fields, which cannot be modified after the object is constructed, can be safely read through
non-synchronized methods, once the object is constructed) This strategy is effective, but can
present problems with liveness, as we'll see later in this lesson.

Intrinsic Locks and Synchronization


Synchronization is built around an internal entity known as the intrinsic lock or monitor lock.
(The API specification often refers to this entity simply as a "monitor.") Intrinsic locks play a
role in both aspects of synchronization: enforcing exclusive access to an object's state and
establishing happens-before relationships that are essential to visibility.
Every object has an intrinsic lock associated with it. By convention, a thread that needs
exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock
before accessing them, and then release the intrinsic lock when it's done with them. A thread is
said to own the intrinsic lock between the time it has acquired the lock and released the lock.
As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other
thread will block when it attempts to acquire the lock.
When a thread releases an intrinsic lock, a happens-before relationship is established between
that action and any subsequent acquistion of the same lock.

Locks In Synchronized Methods


When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for
that method's object and releases it when the method returns. The lock release occurs even if
the return was caused by an uncaught exception.
You might wonder what happens when a static synchronized method is invoked, since a static
method is associated with a class, not an object. In this case, the thread acquires the intrinsic
lock for the Classobject associated with the class. Thus access to class's static fields is
controlled by a lock that's distinct from the lock for any instance of the class.

Synchronized Statements
Another way to create synchronized code is with synchronized statements. Unlike
synchronized methods, synchronized statements must specify the object that provides the
intrinsic lock:
publicvoidaddName(Stringname){
synchronized(this){
lastName=name;
nameCount++;
}
nameList.add(name);
}

In this example, the addNamemethod needs to synchronize changes to lastNameand


nameCount, but also needs to avoid synchronizing invocations of other objects' methods.
(Invoking other objects' methods from synchronized code can create problems that are
described in the section on Liveness.) Without synchronized statements, there would have to be
a separate, unsynchronized method for the sole purpose of invoking nameList.add.
Synchronized statements are also useful for improving concurrency with fine-grained
synchronization. Suppose, for example, class MsLunchhas two instance fields, c1and c2, that
are never used together. All updates of these fields must be synchronized, but there's no reason
to prevent an update of c1 from being interleaved with an update of c2 and doing so reduces
concurrency by creating unnecessary blocking. Instead of using synchronized methods or

otherwise using the lock associated with this, we create two objects solely to provide locks.
publicclassMsLunch{
privatelongc1=0;
privatelongc2=0;
privateObjectlock1=newObject();
privateObjectlock2=newObject();
publicvoidinc1(){
synchronized(lock1){
c1++;
}
}
publicvoidinc2(){
synchronized(lock2){
c2++;
}
}
}

Use this idiom with extreme care. You must be absolutely sure that it really is safe to interleave
access of the affected fields.

Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by another thread. But a thread can acquire a
lock that it already owns. Allowing a thread to acquire the same lock more than once enables
reentrant synchronization. This describes a situation where synchronized code, directly or
indirectly, invokes a method that also contains synchronized code, and both sets of code use
the same lock. Without reentrant synchronization, synchronized code would have to take many
additional precautions to avoid having a thread cause itself to block.

Atomic Access
In programming, an atomic action is one that effectively happens all at once. An atomic action
cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side
effects of an atomic action are visible until the action is complete.
We have already seen that an increment expression, such as c++, does not describe an atomic
action. Even very simple expressions can define complex actions that can decompose into
other actions. However, there are actions you can specify that are atomic:
Reads and writes are atomic for reference variables and for most primitive variables (all
types except longand double).
Reads and writes are atomic for all variables declared volatile(including longand
doublevariables).
Atomic actions cannot be interleaved, so they can be used without fear of thread interference.
However, this does not eliminate all need to synchronize atomic actions, because memory
consistency errors are still possible. Using volatilevariables reduces the risk of memory
consistency errors, because any write to a volatilevariable establishes a happens-before
relationship with subsequent reads of that same variable. This means that changes to a

volatilevariable are always visible to other threads. What's more, it also means that when a

thread reads a volatilevariable, it sees not just the latest change to the volatile, but also the
side effects of the code that led up the change.
Using simple atomic variable access is more efficient than accessing these variables through
synchronized code, but requires more care by the programmer to avoid memory consistency
errors. Whether the extra effort is worthwhile depends on the size and complexity of the
application.
Some of the classes in the java.util.concurrentpackage provide atomic methods that do
not rely on synchronization. We'll discuss them in the section on High Level Concurrency
Objects

Thread-Safe Collections
public static CollectionsynchronizedCollection(Collectionc)
public static ListsynchronizedList(Listl)
public static MapsynchronizedMap(Mapm)
public static SetsynchronizedSet(Sets)
public static SortedMapsynchronizedSortedMap(SortedMapm)
public static SortedSetsynchronizedSortedSet(SortedSets)
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class MainClass{
public static void main(String[]a){
Sets=new HashSet();
s.add("A");
s.add("B");
s.add("C");
s.add("D");

336
s.add("E");
s.add("F");
s.add("H");
Collections.synchronizedSet(s);
}
}

Executors
In all of the previous examples, there's a close connection between the task being done by a
new thread, as defined by its Runnableobject, and the thread itself, as defined by a Thread
object. This works well for small applications, but in large-scale applications, it makes sense to
separate thread management and creation from the rest of the application. Objects that
encapsulate these functions are known as executors. The following subsections describe
executors in detail.
Executor Interfaces define the three executor object types.

Thread Pools are the most common kind of executor implementation

Executor Interfaces
The java.util.concurrentpackage defines three executor interfaces:
Executor, a simple interface that supports launching new tasks.
ExecutorService, a subinterface of Executor, which adds features that help manage
the lifecycle, both of the individual tasks and of the executor itself.
ScheduledExecutorService, a subinterface of ExecutorService, supports future
and/or periodic execution of tasks.
Typically, variables that refer to executor objects are declared as one of these three interface
types, not with an executor class type.

The Executor Interface


The Executorinterface provides a single method, execute, designed to be a drop-in
replacement for a common thread-creation idiom. If ris a Runnableobject, and eis an
Executorobject you can replace
(newThread(r)).start();

with
e.execute(r);

However, the definition of executeis less specific. The low-level idiom creates a new thread
and launches it immediately. Depending on the Executorimplementation, executemay do
the same thing, but is more likely to use an existing worker thread to run r, or to place rin a
queue to wait for a worker thread to become available. (We'll describe worker threads in the
section on Thread Pools.)
The executor implementations in java.util.concurrentare designed to make full use of the
more advanced ExecutorServiceand ScheduledExecutorServiceinterfaces, although they
also work with the base Executorinterface.

The ExecutorService Interface


The ExecutorServiceinterface supplements executewith a similar, but more versatile
submitmethod. Like execute, submitaccepts Runnableobjects, but also accepts Callable
objects, which allow the task to return a value. The submitmethod returns a Futureobject,
which is used to retrieve the Callablereturn value and to manage the status of both Callable
and Runnabletasks.
ExecutorServicealso provides methods for submitting large collections of Callableobjects.
Finally, ExecutorServiceprovides a number of methods for managing the shutdown of the
executor. To support immediate shutdown, tasks should handle interrupts correctly.

The ScheduledExecutorService Interface


The ScheduledExecutorServiceinterface supplements the methods of its parent
ExecutorServicewith schedule, which executes a Runnableor Callabletask after a
specified delay. In addition, the interface defines scheduleAtFixedRateand

scheduleWithFixedDelay, which executes specified tasks repeatedly, at defined intervals.

Thread Pools
Most of the executor implementations in java.util.concurrentuse thread pools, which
consist of worker threads. This kind of thread exists separately from the Runnableand
Callabletasks it executes and is often used to execute multiple tasks.
Using worker threads minimizes the overhead due to thread creation. Thread objects use a
significant amount of memory, and in a large-scale application, allocating and deallocating
many thread objects creates a significant memory management overhead.
One common type of thread pool is the fixed thread pool. This type of pool always has a
specified number of threads running; if a thread is somehow terminated while it is still in use, it
is automatically replaced with a new thread. Tasks are submitted to the pool via an internal
queue, which holds extra tasks whenever there are more active tasks than threads.
An important advantage of the fixed thread pool is that applications using it degrade
gracefully. To understand this, consider a web server application where each HTTP request is
handled by a separate thread. If the application simply creates a new thread for every new
HTTP request, and the system receives more requests than it can handle immediately, the
application will suddenly stop responding to all requests when the overhead of all those threads
exceed the capacity of the system. With a limit on the number of the threads that can be
created, the application will not be servicing HTTP requests as quickly as they come in, but it
will be servicing them as quickly as the system can sustain.
A simple way to create an executor that uses a fixed thread pool is to invoke the
newFixedThreadPoolfactory method in java.util.concurrent.ExecutorsThis class also
provides the following factory methods:
The newCachedThreadPoolmethod creates an executor with an expandable thread
pool. This executor is suitable for applications that launch many short-lived tasks.
The newSingleThreadExecutormethod creates an executor that executes a single task

at a time.
Several factory methods are ScheduledExecutorServiceversions of the above
executors.
If none of the executors provided by the above factory methods meet your needs, constructing
instances of java.util.concurrent.ThreadPoolExecutoror
java.util.concurrent.ScheduledThreadPoolExecutorwill give you additional options.

Thread & Event Driven Programming


Methods

Threading

Event
Driven
Programming

Model

A process contains multiple


threads: Each thread may
computable or blocked at
any instant in time.

A process consists of the


processing
of a series of events. At any
instant

Examples:
DECthreads, Windows NT

Process

Lightweight :
Separate Register Set
Separate Stack
Separately Dispatchable
Preemptable
Low resource
consumption

Method

If no interactions, simple
application implementation. If
there are interactions between
threads, then the interactions
require locking and other
mechanisms to prevent subtle
programming errors from
causing failures.

Implementation

Preemption model
Priority model
Debugging
Application suitability

in time, a single event is being


processed.
Examples:
OpenVMS AST, X-Windows
Featherweight
Shared Register Set
Shared (nested) Stack
Separate Address Space

Extremely inexpensive
Creation
No preemption
Implicit synchronization
Extremely inexpensive
Each event is treated as an
independent transaction by the
program. Pre-emption is not
permitted, so there are no locks
or
synchronization required to
access data structures during
the processing
of a single event.
FIFO
Non-preemptable
Featherweight

You might also like