Multithreading Concept: by The End of This Chapter, You Will Be Able To
Multithreading Concept: by The End of This Chapter, You Will Be Able To
Multithreading Concept
By the end of this chapter, you will be able to:
• Understand multithreading fundamentals
• Create a thread
In the rare situation that you have a single-core CPU in your box, the OS will switch the
CPU between these processes, giving you the illusion of concurrently running processes.
Processes are usually large and can be further split into smaller units of execution.
For example,
• A word processing application might perform a background spell check while the
user edits the document.
• A spreadsheet application may allow the user to interact with it while it performs
calculations in the background.
• To implement these kinds of features, the developer splits the process into
two units—one that is responsible for calculations and the other for handling
user interactions.
However, shared variables make communication between threads more efficient and
easier to program than interprocess communication.
Moreover, on some operating systems, threads are more “lightweight” than processes—
it takes less overhead to create and destroy individual threads than it does to launch
new processes
Programs that can run more than one thread at once are said to be multithreaded.
A multithreaded program contains two or more parts that can run concurrently.
Each part of such a program is called a thread, and each thread defines a separate path
of execution
(a) Here multiple threads are running on multiple CPUs. (b) Here multiple threads share
a single CPU.
The operating system is responsible for scheduling and allocating resources to threads
4
Thread State
Threads can be in one of six states:
New Threads
When you create a thread with the new operator—for example, new Thread(r)—the
thread is not yet running. This means that it is in the new state.
When a thread is in the new state, the program has not started executing code inside of
it.
Runnable Threads
Once you invoke the start method, the thread is in the runnable state. A runnable
thread may or may not actually be running. It is up to the operating system to give the
thread time to run.
The details of thread scheduling depend on the services that the operating system
provides. 5
Preemptive scheduling systems give each runnable thread a slice of time to perform its
task.
When that slice of time is exhausted, the operating system preempts the thread and
gives another thread an opportunity to work.
When selecting the next thread, the operating system takes into account the thread
priorities.
Blocked Threads
When the thread tries to acquire an intrinsic object lock that is currently held by another
thread, it becomes blocked.
The thread becomes unblocked when all other threads have relinquished the lock and
the thread scheduler has allowed this thread to hold it
Waiting Threads
When the thread waits for another thread to notify the scheduler of a condition, it enters
the waiting state.
In practice, the difference between the blocked and waiting state is not significant. 6
Timed Waiting Threads
Several methods have a timeout parameter.
Calling them causes the thread to enter the timed waiting state.
This state persists either until the timeout expires or the appropriate notification has been
received
Terminated Threads
A thread is terminated for one of two reasons:
• It dies a natural death because the run method exits normally.
• It dies abruptly because an uncaught exception terminates the run method.
In particular, you can kill a thread by invoking its stop method.
That method throws a ThreadDeath error object that kills the thread. However, the stop
method is deprecated, and you should never call it in your own code.
7
Thread states 8
Thread Priorities
With several threads running on a system, you may want to prioritize their execution.
You do so by assigning a priority level to each thread. In Java, the thread priority levels
range from 1 to 10.
The priority 0 is reserved for the virtual machine. Java also provides a few predefined
constants for setting the thread priority.
The OS maintains a separate queue for all threads belonging to each priority level
9
Because threads with the higher priority level get the CPU first, this implies that a thread
with a lower priority will starve for CPU cycles forever if the higher-priority threads never
finish.
However, this is not exactly true. The OS periodically raises the priority of these
“starving” threads until they reach the currently executing thread priority level.
At this level, each thread will eventually get its time slice.
10
After the time slice is over, the thread will be returned to the queue of its original priority.
There are two types of threads in Java:
• Daemon threads
• Non-daemon (user) threads
A daemon thread terminates automatically as soon as the parent thread that created this
thread terminates.
A daemon is simply a thread that has no other role in life than to serve others.
Examples are timer threads that send regular “timer ticks” to other threads or threads
that clean up stale cache entries.
A daemon thread should never access a persistent resource such as a file or database
since it can terminate at any time, even in the middle of an operation.
11
A non-daemon thread, however, continues to live even when the parent dies.
As long as there is at least one thread alive, we say that the process is alive.
When all non-daemon threads along with the remaining daemon threads of an
application die, we say that the process is dead.
Every Java application has at least one non-daemon thread, which is the main thread.
When the program quits this thread or the user closes the application, the main thread
dies.
However, the process can continue to live if it has already spawned other non-daemon
threads.
Creating a Thread
Java implements thread functionality by using the following classes/interfaces:
• The interface Runnable
• The class Thread
• The class ThreadGroup
12
The Runnable interface abstracts a unit of executable code. You can construct a thread
on any object that implements the Runnable interface.
Runnable defines only one method called run( ), which is declared like this:
Inside run( ), you will define the code that constitutes the new thread.
It is important to understand that run( ) can call other methods, use other classes,
and declare variables just like the main thread.
The only difference is that run( ) establishes the entry point for another, concurrent
thread of execution within your program.
After you have created a class that implements Runnable, you will instantiate an object
of type Thread on an object of that class.
13
For example
All Swing components must be configured from the event dispatch thread, the thread of
control that passes events such as mouse clicks and keystrokes to the user interface
components.
The following code fragment is used to execute statements in the event dispatch thread:
EventQueue.invokeLater(new Runnable()
{
});
14
The Thread class has several constructors, which take in a variation of the following
parameters:
String name.
Every Thread object has a name associated with it. You can assign a thread any name
you like because the purpose of the name is to allow you to distinguish the various
threads you create.
If you do not assign your threads a name, the Thread class names them Thread0,
Thread1, Thread2, and so on.
Runnable target.
Associates a Runnable object as the target of the Thread.
ThreadGroup.
The group that the thread belongs to
long stackSize.
The number of bytes you want allocated for the size of the stack used by this thread.
15
Here is a simple procedure for running a task in a separate thread:
1. Place the code for the task into the run method of a class that implements the
Runnable interface. That interface is very simple, with a single method:
{
void run();
}
You simply implement a class, like this:
class MyRunnable implements Runnable
{
public void run()
task code
}
}
16
2. Construct an object of your class:
Runnable r = new MyRunnable();
When a Java program starts up, one thread begins running immediately.
This is usually called the main thread of your program, because it is the one that is
executed when your program begins.
Caution
Do not call the run method of the Thread class or the Runnable object.
Calling the run method directly merely executes the task in the same thread—no new thread is
started. Instead, call the Thread.start method. It will create a new thread that executes the
21
run method.
It is possible to have a thread begin execution as soon as it is created.
Second, there is no need for MyThread to store the name of the thread since it is
possible to give a name to a thread when it is created.
Example
public class MyThread2 extends Thread {
// Construct a new thread.
MyThread2(String name) {
super(name); // name thread
start(); // start the thread
}
public void run() {
System.out.println(getName() + " starting.");
try {
25
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(getName() + " interrupted.");
}
System.out.println(getName() + " terminating.");
}
}
26
public class ExtendThread {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread2 mt = new MyThread2("Child #1");
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
27
Sample output
28
// Create multiple threads.
public class MyThread3 implements Runnable {
Thread thrd;
// Construct a new thread.
MyThread3(String name) {
thrd = new Thread(this, name);
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
29
}}
public class MoreThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread3 mt1 = new MyThread3("Child #1");
Create and start
MyThread3 mt2 = new MyThread3("Child #2"); executing three threads.
MyThread3 mt3 = new MyThread3("Child #3");
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
30
Sample output
As you can see, once started, all three
child threads share the CPU.
31
Determining When a Thread Ends
Thread provides two means by which you can determine if a thread has ended. First,
you can call isAlive( ) on the thread. Its general form is shown here:
final boolean isAlive( )
The isAlive( ) method returns true if the thread upon which it is called is still running.
It returns false otherwise.
To try isAlive( ), substitute this version of MoreThreads for the one shown in the
preceding program:
Example
32
public class MoreThreads4 {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread3 mt1 = new MyThread3("Child #1");
MyThread3 mt2 = new MyThread3("Child #2");
MyThread3 mt3 = new MyThread3("Child #3");
do {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
} while (mt1.thrd.isAlive() ||
This waits until all threads
mt2.thrd.isAlive() || terminate
mt3.thrd.isAlive());
System.out.println("Main thread ending.");
}
33
}
This version produces output that is similar to the previous version, except that main( )
ends as soon as the other threads finish.
The difference is that it uses isAlive( ) to wait for the child threads to terminate.
Another way to wait for a thread to finish is to call join( ), shown here:
final void join( ) throws InterruptedException
Additional forms of join( ) allow you to specify a maximum amount of time that you
want to wait for the specified thread to terminate.
Here is a program that uses join( ) to ensure that the main thread is the last to stop:
34
// Use join().
public class MyThreadJoin implements Runnable {
Thread thrd;
// Construct a new thread.
MyThreadJoin(String name) {
thrd = new Thread(this, name);
thrd.start(); // start the thread
}
// Begin execution of new thread.
@Override
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
} 35
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
}
}
In general,
The join method allows one thread to wait for the completion of another. If t is a Thread
object whose thread is currently executing,
t.join();
causes the current thread to pause execution until t's thread terminates.
37
Sample output from this program is shown here. Remember that when you try the
program, your precise output may vary slightly.
38
Thread Priorities (revisited…)
A thread’s priority determines how much CPU time a thread receives relative to the other
active threads.
In general, over a given period of time, low-priority threads receive little. High-priority
threads receive a lot.
It is important to understand that factors other than a thread’s priority also affect how
much CPU time a thread receives.
For example, if a high-priority thread is waiting on some resource, perhaps for keyboard
input, then it will be blocked, and a lower priority thread will run.
However, when that high-priority thread gains access to the resource, it can preempt the
low-priority thread and resume execution.
Another factor that affects the scheduling of threads is the way the operating system
implements multitasking.
Thus, just because you give one thread a high priority and another a low priority does
not necessarily mean that one thread will run faster or more often than the other. It’s just
39
that the high-priority thread has greater potential access to the CPU.
You can change a thread’s priority by calling setPriority( ), which is a member of
Thread. This is its general form:
You can obtain the current priority setting by calling the getPriority( ) method of
Thread, shown here:
final int getPriority( )
Example
// Demonstrate thread priorities.
public class Priority implements Runnable {
int count;
Thread thrd;
static boolean stop = false;
static String currentName;
/* Construct a new thread. Notice that this
constructor does not actually start the
40
threads running. */
Priority(String name) {
thrd = new Thread(this, name);
count = 0;
currentName = name;
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
do {
count++;
if(currentName.compareTo(thrd.getName()) != 0) {
currentName = thrd.getName();
System.out.println("In " + currentName);
} The first thread to
} while(stop == false && count < 100); 100 stops
stop = true; all threads.
System.out.println("\n" + thrd.getName() +
" terminating.");
41
}}
class PriorityDemo {
public static void main(String args[]) {
Priority mt1 = new Priority("High Priority");
Priority mt2 = new Priority("Low Priority");
// set the priorities Give mt1 a
mt1.thrd.setPriority(Thread.NORM_PRIORITY+2); higher
mt2.thrd.setPriority(Thread.NORM_PRIORITY-2); priority
than mt2.
// start the threads
mt1.thrd.start();
mt2.thrd.start();
try {
mt1.thrd.join();
mt2.thrd.join();
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
System.out.println("\nHigh priority thread counted to " +
42
mt1.count);
System.out.println("Low priority thread counted to " +
mt2.count);
}
}}
Here is a sample run for 100 loops
In this run, the high-priority thread got a vast majority of the CPU time.
Of course, the exact output produced by this program will depend upon the speed of your
CPU, the number of CPUs in your system, the operating system you are using, and the
number of other tasks running in the system. 43
Synchronization
When using multiple threads, it is sometimes necessary to coordinate the activities of two
or more.
The most common reason for synchronization is when two or more threads need access to a
shared resource that can be used by only one thread at a time.
For example, when one thread is writing to a file, a second thread must be prevented from
doing so at the same time.
What happens if two threads have access to the same object and each calls a method that
modifies the state of the object?
Depending on the order in which the data were accessed, corrupted objects can result. Such
a situation is often called a race condition.
Key to synchronization in Java is the concept of the monitor, which controls access to an
object.
44
A monitor works by implementing the concept of a lock.
When an object is locked by one thread, no other thread can gain access to the object.
When the thread exits, the object is unlocked and is available for use by another thread.
There are two ways that you can synchronize your code.
When that method is called, the calling thread enters the object’s monitor, which then
locks the object.
While locked, no other thread can enter the method, or enter any other synchronized
method defined by the object’s class.
When the thread returns from the method, the monitor unlocks the object, allowing it to
be used by the next thread.
45
Example
// Use synchronize to control access.
public class SumArray {
private int sum;
synchronized int sumArray(int nums[]) {
sum = 0; // reset sum
for(int i=0; i<nums.length; i++) {
sum += nums[i];
System.out.println("Running total for " +
Thread.currentThread().getName() +" is " + sum);
try {
Thread.sleep(10); // allow task-switch
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
return sum;
}
}
46
public class MyThreadArray implements Runnable {
Thread thrd;
static SumArray sa = new SumArray();
int a[];
int answer;
// Construct a new thread.
MyThreadArray(String name, int nums[]) {
thrd = new Thread(this, name);
a = nums;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
answer = sa.sumArray(a);
System.out.println("Sum for " + thrd.getName() +
" is " + answer);
System.out.println(thrd.getName() + " terminating.");
}
}
47
public class Sync {
public static void main(String args[]) {
int a[] = {1, 2, 3, 4, 5};
MyThreadArray mt1 = new MyThreadArray("Child #1", a);
MyThreadArray mt2 = new MyThreadArray("Child #2", a);
try {
mt1.thrd.join();
mt2.thrd.join();
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}
48
Sample output (The precise output may differ on your computer.)
49
b. The synchronized Statement
Although creating synchronized methods within classes that you create is an easy and
effective means of achieving synchronization, it will not work in all cases.
For example, you might want to synchronize access to some method that is not modified
by synchronized.
This can occur because you want to use a class that was not created by you but by a
third party, and you do not have access to the source code.
Thus, it is not possible for you to add synchronized to the appropriate methods within the
class.
You simply put calls to the methods defined by this class inside a synchronized block.
Once a synchronized block has been entered, no other thread can call a synchronized
method on the object referred to by objref until the block has been exited.
For example, another way to synchronize calls to sumArray( ) is to call it from within
a synchronized block, as shown in this version of the program:
This version produces the same, correct output as the one shown earlier that uses a
synchronized method.
One of the most exciting features of the concurrent API (which are packaged in
java.util.concurrent) is the Fork/Join Framework.
The Fork/Join Framework supports what is often termed parallel programming
(computers that contain two or more processors, including multicore systems).
The concurrency utilities in general, and the Fork/Join Framework specifically, are
features that you will want to explore after you have become more experienced with
multithreading.
53
Thread Communication Using notify( ), wait( ), and notifyAll( )
The wait( ), notify( ), and notifyAll( ) methods are part of all objects
because they are implemented by the Object class.
This causes the thread to go to sleep and the monitor for that object to be released,
allowing another thread to use the object.
At a later point, the sleeping thread is awakened when some other thread enters the
same monitor and calls notify( ), or notifyAll( ).
54
Following are the various forms of wait( ) defined by Object:
A call to notifyAll( ) notifies all threads, with the highest priority thread gaining
access to the object.
56
synchronized void tock(boolean running) {
if(!running) { // stop the clock
state = "tocked";
notify(); // notify any waiting threads
return;
}
System.out.println("Tock");
state = "tocked"; // set the current state to tocked
notify(); // let tick() run
try {
while(!state.equals("ticked"))
wait(); // wait for tick to complete
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
}
57
class MyClockThread implements Runnable {
Thread thrd;
TickTock ttOb;
// Construct a new thread.
MyClockThread(String name, TickTock tt) {
thrd = new Thread(this, name);
ttOb = tt;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
if(thrd.getName().compareTo("Tick") == 0) {
for(int i=0; i<5; i++)
ttOb.tick(true);
ttOb.tick(false);
}
else {
for(int i=0; i<5; i++)
ttOb.tock(true);
ttOb.tock(false);
}
}
}
58
public class ThreadCom {
public static void main(String args[]) {
TickTock tt = new TickTock();
MyClockThread mt1 = new MyClockThread("Tick", tt);
MyClockThread mt2 = new MyClockThread("Tock", tt);
try {
mt1.thrd.join();
mt2.thrd.join();
} catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}
I have heard the term deadlock applied to misbehaving multithreaded programs. What is
it, and how can I avoid it? Also, what is a race condition, and how can I avoid that, too?
A:
Deadlock is, as the name implies, a situation in which one thread is waiting for another
thread to do something, but that other thread is waiting on the first.
Thus, both threads are suspended, waiting on each other, and neither executes.
The cause of the deadlock often is not readily understood just by looking at the source
code to the program because concurrently executing threads can interact in complex
ways at run time.
A race condition occurs when two (or more) threads attempt to access a shared
resource at the same time, without proper synchronization.
For example, one thread may be writing a new value to a variable while another thread
is incrementing the variable’s current value. 60
Without synchronization, the new value of the variable will depend upon the order in
which the threads execute.
(Does the second thread increment the original value or the new value written by the first
thread?)
In situations like this, the two threads are said to be “racing each other,” with the final
outcome determined by which thread finishes first.
Prior to Java 2, a program used suspend( ), resume( ), and stop( ), which are
methods defined by Thread, to pause, restart, and stop the execution of a thread.
61
final void resume( )
final void suspend( )
final void stop( )
This was done because suspend( ) can sometimes cause serious problems that
involve deadlock.
The resume( ) method is also deprecated. It does not cause problems but cannot be
used without the suspend( ) method as its counterpart.
The stop( ) method of the Thread class was also deprecated by Java 2. This was
done because this method too can sometimes cause serious problems.
For the stop flag, if it is set to “stop,” the thread must terminate.
The following example shows one way to implement your own versions of suspend( ),
resume( ), and stop( ):
63
// This is the entry point for thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int i = 1; i < 1000; i++) {
System.out.print(i + " ");
if((i%10)==0) {
System.out.println();
Thread.sleep(250);
}
// Use synchronized block to check suspended and stopped.
synchronized(this) {
while(suspended) {
wait();
}
if(stopped) This synchronized block checks
break; suspended and stopped.
}
}
} catch (InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " exiting.");
} 64
// Stop the thread.
synchronized void mystop() {
stopped = true;
// The following ensures that a suspended thread can be
stopped.
suspended = false;
notify();
}
// Suspend the thread.
synchronized void mysuspend() {
suspended = true;
}
// Resume the thread.
synchronized void myresume() {
suspended = false;
notify();
}
}
65
class Suspend {
public static void main(String args[]) {
MySRSThread ob1 = new MySRSThread("My Thread");
try {
Thread.sleep(1000); // let ob1 thread start executing
ob1.mysuspend();
System.out.println("Suspending thread.");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread.");
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Suspending thread.");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread.");
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Stopping thread.");
ob1.mystop();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
66
// wait for thread to finish
try {
ob1.thrd.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
Example
My Thread starting.
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
Suspending thread.
Resuming thread.
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
Suspending thread.
Resuming thread.
81 82 83 84 85 86 87 88 89 90 67
91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110
111 112 113 114 115 116 117 118 119 120
Stopping thread.
My Thread exiting.
Main thread exiting.
Deadlock
• Deadlock describes a situation where two or more threads are blocked forever,
waiting for each other.
• This is like when two people are standing at a door, both holding it open for the other
to pass through.
Thread Thread
2 1
Rsrc
B
In this figure, the two threads perform synchronized access to their code. Consider the
situation where Thread 1 obtains a lock on Object A and then loses the CPU. Now
Thread 2, which gains the CPU, obtains a lock on Object B and executes a few code
lines before losing the CPU. Thread 1, which now has the CPU, proceeds with its code
to obtain a lock on Object B. However, because Object B has been locked by Thread 2,
it cannot proceed and has to wait until the lock on Object B is released by Thread 2.
Now, Thread 2 gains the CPU one more time and proceeds with its own code, in which it
tries to obtain a lock on Object A. Because Object A has previously been locked by
Thread 1, Thread 2 cannot continue unless the lock on Object A is released by Thread 1.
You can see that each thread must wait on the other to release its lock and therefore
neither can proceed. This situation is called deadlock.
69
Example
package com.assignment;
/**
*
* @author Administrator
*/
//main class
public class ThreadWithDeadlock {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
70
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
71
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
When you compile and execute the
above program, you find a
deadlock situation and following is
the output produced by the
program
72
The above program will hang forever because neither of the threads in position to
proceed and waiting for each other to release the lock, so you can come out of the
program by pressing CTRL+C.
Solutions to Deadlock
Deadlocks have no simple solution. They can be avoided only by careful
coding. However, three techniques can be used to help you in detecting and
preventing deadlocks in your code:
Lock Ordering
• In our earlier example, deadlock can be avoided by maintaining the order in
which the locks are obtained and released by two threads.
• If locks on multiple objects are obtained and released in the same order by
both threads, a deadlock cannot occur.
• Thus, if both threads in our example obtain the locks in the order Object A
first, followed by Object B, a deadlock will not occur.
• The general rule here is that in case of multiple locks, if all locks are always
taken in the same order by any thread, deadlocks cannot occur.
73
Example ( A solution to the previous example)
Let's change the order of the lock and run of the same program to see if both
the threads still wait for each other
package com.assignment;
/**
*
* @author Administrator
*/
public class ThreadWithoutDeadlock {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
}
74
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(10);
}catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
75
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1...");
try {
Thread.sleep(10);
}catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
} So just changing the order of the
locks prevent the program in going
into a deadlock situation and
completes with the following result
76
Lock Timeout
• Putting a timeout on lock attempts helps in preventing a deadlock situation.
• If a thread does not succeed in obtaining all the necessary locks within the
given timeout period, it will back up, freeing all locks taken so far. It then
waits for a random amount of time before making another attempt to obtain
the locks.
Deadlock Detection
• If both these remedies of lock ordering and timeout are not feasible, we can
resort to deadlock detection, which is definitely a more difficult solution of
deadlock prevention.
• In this solution, we record every request and acquire a lock by all threads.
78