SlideShare a Scribd company logo
Concurrency in Java- Multi Threading
Back in the old days a computer had a single CPU, and was only capable of
executing a single program at a time. Later came multitasking which meant
that computers could execute multiple programs (AKA tasks or processes) at
the same time. It wasn't really "at the same time" though. The single CPU was
shared between the programs. The operating system would switch between
the programs running, executing each of them for a little while before
switching.
Along with multitasking came new challenges for software developers.
Programs can no longer assume to have all the CPU time available, nor all
memory or any other computer resources. A "good citizen" program should
release all resources it is no longer using, so other programs can use them.
Later yet came multithreading which mean that you could have multiple
threads of execution inside the same program. A thread of execution can be
thought of as a CPU executing the program. When you have multiple threads
executing the same program, it is like having multiple CPUs execute within the
same program.
Multithreading can be a great way to increase the performance of some types
of programs. However, mulithreading is even more challenging than
multitasking. The threads are executing within the same program and are
hence reading and writing the same memory simultanously. This can result in
errors not seen in a singlethreaded program. Some of these errors may not be
seen on single CPU machines, because two threads never really execute
"simultanously". Modern computers, though, come with multi core CPUs, and
even with multiple CPUs too. This means that separate threads can be
executed by separate cores or CPUs simultanously.
If a thread reads a memory location while another thread writes to it, what
value will the first thread end up reading? The old value? The value written by
the second thread? Or a value that is a mix between the two? Or, if two
threads are writing to the same memory location simultanously, what value
will be left when they are done? The value written by the first thread? The
value written by the second thread? Or a mix of the two values written?
Without proper precautions any of these outcomes are possible. The
behaviour would not even be predictable. The outcome could change from
time to time. Therefore it is important as a developer to know how to take the
right precautions - meaning learning to control how threads access shared
resources like memory, files, databases etc. That is one of the topics this Java
concurrency tutorial addresses.
Multithreading and Concurrency in Java
Java was one of the first languages to make multithreading easily available to
developers. Java had multithreading capabilities from the very beginning.
Therefore, Java developers often face the problems described above. That is
the reason I am writing this trail on Java concurrency. As notes to myself, and
any fellow Java developer whom may benefit from it.
The trail will primarily be concerned with multithreading in Java, but some of
the problems occurring in multithreading are similar to problems occurring in
multitasking and in distributed systems. References to multitasking and
distributed systems may therefore occur in this trail too. Hence the word
"concurrency" rather than "multithreading".
MultiThreading Benefits
The reason multithreading is still used in spite of its challenges is that
multithreading can have several benefits. Some of these benefits are:
 Better resource utilization.
 Simpler program design in some situations.
 More responsive programs.
Better resource utilization
Imagine an application that reads and processes files from the local file
system. Lets say that reading af file from disk takes 5 seconds and processing
it takes 2 seconds. Processing two files then takes
5 seconds reading file A
2 seconds processing file A
5 seconds reading file B
2 seconds processing file B
-----------------------
14 seconds total
When reading the file from disk most of the CPU time is spent waiting for the
disk to read the data. The CPU is pretty much idle during that time. It could be
doing something else. By changing the order of the operations, the CPU could
be better utilized. Look at this ordering:
5 seconds reading file A
5 seconds reading file B + 2 seconds processing file A
2 seconds processing file B
-----------------------
12 seconds total
The CPU waits for the first file to be read. Then it starts the read of the second
file. While the second file is being read, the CPU processes the first file.
Remember, while waiting for the file to be read from disk, the CPU is mostly
idle.
In general, the CPU can be doing other things while waiting for IO. It doesn't
have to be disk IO. It can be network IO as well, or input from a user at the
machine. Network and disk IO is often a lot slower than CPU's and memory
IO.
Simpler Program Design
If you were to program the above ordering of reading and processing by hand
in a singlethreaded application, you would have to keep track of both the read
and processing state of each file. Instead you can start two threads that each
just reads and processes a single file. Each of these threads will be blocked
while waiting for the disk to read its file. While waiting, other threads can use
the CPU to process the parts of the file they have already read. The result is,
that the disk is kept busy at all times, reading from various files into memory.
This results in a better utilization of both the disk and the CPU. It is also easier
to program, since each thread only has to keep track of a single file.
More responsive programs
Another common goal for turning a singlethreaded application into a
multithreaded application is to achieve a more responsive application. Imagine
a server application that listens on some port for incoming requests. when a
request is received, it handles the request and then goes back to listening.
The server loop is sketched below:
while(server is active){
listen for request
process request
}
If the request takes a long time to process, no new clients can send requests
to the server for that duration. Only while the server is listening can requests
be received.
An alternate design would be for the listening thread to pass the request to a
worker thread, and return to listening immediatedly. The worker thread will
process the request and send a reply to the client. This design is sketched
below:
while(server is active){
listen for request
hand request to worker thread
}
This way the server thread will be back at listening sooner. Thus more clients
can send requests to the server. The server has become more responsive.
The same is true for desktop applications. If you click a button that starts a
long task, and the thread executing the task is the thread updating the
windows, buttons etc., then the application will appear unresponsive while the
task executes. Instead the task can be handed off to a worker thread. While
the worker thread is busy with the task, the window thread is free to respond
to other user requests. When the worker thread is done it signals the window
thread. The window thread can then update the application windows with the
result of the task. The program with the worker thread design will appear more
responsive to the user.
Creating and Starting Threads
Creating a thread in Java is done like this:
Thread thread = new Thread();
To start the Java thread you will call its start() method, like this:
thread.start();
This example doesn't specify any code for the thread to execute. The thread
will stop again right away after it is started.
There are two ways to specify what code the thread should execute. The first
is to create a subclass of Thread and override the run() method. The second
method is to pass an object that implements Runnable(java.lang.Runnable to
the Thread constructor. Both methods are covered below.
Thread Subclass
The first way to specify what code a thread is to run, is to create a subclass of
Thread and override therun() method. The run() method is what is executed
by the thread after you call start(). Here is an example of creating a
Java Thread subclass:
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
}
}
To create and start the above thread you can do like this:
MyThread myThread = new MyThread();
myTread.start();
The start() call will return as soon as the thread is started. It will not wait until
the run() method is done. The run() method will execute as if executed by a
different CPU. When the run() method executes it will print out the text
"MyThread running".
You can also create an anonymous subclass of Thread like this:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
}
}
thread.start();
This example will print out the text "Thread running" once the run() method is
executed by the new thread.
Runnable Interface Implementation
The second way to specify what code a thread should run is by creating a
class that implementsjava.lang.Runnable. The Runnable object can be executed
by a Thread.
Here is a Java Runnable example:
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
}
}
To have the run() method executed by a thread, pass an instance
of MyRunnable to a Thread in its constructor. Here is how that is done:
Thread thread = new Thread(new MyRunnable());
thread.start();
When the thread is started it will call the run() method of
the MyRunnable instance instead of executing it's own run() method. The above
example would print out the text "MyRunnable running".
You can also create an anonymous implementation of Runnable, like this:
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
}
}
Thread thread = new Thread(myRunnable);
thread.start();
Subclass or Runnable?
There are no rules about which of the two methods that is the best. Both
methods works. Personally though, I prefer implementing Runnable, and
handing an instance of the implementation to a Threadinstance. When having
the Runnable's executed by a thread pool it is easy to queue up
the Runnableinstances until a thread from the pool is idle. This is a little harder
to do with Thread subclasses.
Sometimes you may have to implement Runnable as well as subclass Thread.
For instance, if creating a subclass of Thread that can execute more than
one Runnable. This is typically the case when implementing a thread pool.
Common Pitfall: Calling run() Instead of start()
When creating and starting a thread a common mistake is to call
the run() method of the Thread instead ofstart(), like this:
Thread newThread = new Thread(MyRunnable());
newThread.run(); //should be start();
At first you may not notice anything because the Runnable's run() method is
executed like you expected. However, it is NOT executed by the new thread
you just created. Instead the run() method is executed by the thread that
created the thread. In other words, the thread that executed the above two
lines of code. To have the run() method of the MyRunnable instance called by
the new created thread, newThread, you MUST call
the newThread.start() method.
Thread Names
When you create a Java thread you can give it a name. The name can help
you distinguish different threads from each other. For instance, if multiple
threads write to System.out it can be handy to see which thread wrote the text.
Here is an example:
Thread thread = new Thread("New Thread") {
public void run(){
System.out.println("run by: " + getName());
}
};
thread.start();
System.out.println(thread.getName());
Notice the string "New Thread" passed as parameter to the Thread constructor.
This string is the name of the thread. The name can be obtained via
the Thread's getName() method. You can also pass a name to aThread when
using a Runnable implementation. Here is how that looks:
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable, "New Thread");
thread.start();
System.out.println(thread.getName());
Notice however, that since the MyRunnable class is not a subclass of Thread, it
does not have access to thegetName() method of the thread executing it.
Thread.currentThread()
The Thread.currentThread() method returns a reference to the Thread instance
executing currentThread() . This way you can get access to the
Java Thread object representing the thread executing a given block of code.
Here is an example of how to use Thread.currentThread() :
Thread thread = Thread.currentThread();
Once you have a reference to the Thread object, you can call methods on it.
For instance, you can get the name of the thread currently executing the code
like this:
String threadName = Thread.currentThread().getName();
Java Thread Example
Here is a small example. First it prints out the name of the thread executing
the main() method. This thread is assigned by the JVM. Then it starts up 10
threads and give them all a number as name ("" + i). Each thread then prints
its name out, and then stops executing.
public class ThreadExample {
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());
for(int i=0; i<10; i++){
new Thread("" + i){
public void run(){
System.out.println("Thread: " + getName() + " running");
}
}.start();
}
}
}
Note that even if the threads are started in sequence (1, 2, 3 etc.) they may
not execute sequentially, meaning thread 1 may not be the first thread to write
its name to System.out. This is because the threads are in principle executing
in parallel and not sequentially. The JVM and/or operating system determines
the order in which the threads are executed. This order does not have to be
the same order in which they were started.
Critical Section and Race Condition
A race condition is a special condition that may occur inside a critical section.
A critical section is a section of code that is executed by multiple threads and
where the sequence of execution for the threads makes a difference in the
result of the concurrent execution of the critical section.
When the result of multiple threads executing a critical section may differ
depending on the sequence in which the threads execute, the critical section
is said to contain a race condition. The term race condition stems from the
metaphor that the threads are racing through the critical section, and that the
result of that race impacts the result of executing the critical section.
This may all sound a bit complicated, so I will elaborate more on race
conditions and critical sections in the following sections.
Critical Sections
Running more than one thread inside the same application does not by itself
cause problems. The problems arise when multiple threads access the same
resources. For instance the same memory (variables, arrays, or objects),
systems (databases, web services etc.) or files.
In fact, problems only arise if one or more of the threads write to these
resources. It is safe to let multiple threads read the same resources, as long
as the resources do not change.
Here is a critical section Java code example that may fail if executed by
multiple threads simultaneously:
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}
Imagine if two threads, A and B, are executing the add method on the same
instance of the Counter class. There is no way to know when the operating
system switches between the two threads. The code in theadd() method is not
executed as a single atomic instruction by the Java virtual machine. Rather it
is executed as a set of smaller instructions, similar to this:
1. Read this.count from memory into register.
2. Add value to register.
3. Write register to memory.
Observe what happens with the following mixed execution of threads A and B:
this.count = 0;
A: Reads this.count into a register (0)
B: Reads this.count into a register (0)
B: Adds value 2 to register
B: Writes register value (2) back to memory. this.count now equals 2
A: Adds value 3 to register
A: Writes register value (3) back to memory. this.count now equals 3
The two threads wanted to add the values 2 and 3 to the counter. Thus the
value should have been 5 after the two threads complete execution. However,
since the execution of the two threads is interleaved, the result ends up being
different.
In the execution sequence example listed above, both threads read the value
0 from memory. Then they add their i ndividual values, 2 and 3, to the value,
and write the result back to memory. Instead of 5, the value left
in this.count will be the value written by the last thread to write its value. In the
above case it is thread A, but it could as well have been thread B.
Race Conditions in Critical Sections
The code in the add() method in the example earlier contains a critical section.
When multiple threads execute this critical section, race conditions occur.
More formally, the situation where two threads compete for the same
resource, where the sequence in which the resource is accessed is
significant, is called race conditions. A code section that leads to race
conditions is called a critical section.
Preventing Race Conditions
To prevent race conditions from occurring you must make sure that the critical
section is executed as an atomic instruction. That means that once a single
thread is executing it, no other threads can execute it until the first thread has
left the critical section.
Race conditions can be avoided by proper thread synchronization in critical
sections. Thread synchronization can be achieved using a synchronized
block of Java code. Thread synchronization can also be achieved using
other synchronization constructs like locks or atomic variables
likejava.util.concurrent.atomic.AtomicInteger.
Critical Section Throughput
For smaller critical sections making the whole critical section a synchronized
block may work. But, for larger critical sections it may be beneficial to break
the critical section into smaller critical sections, to allow multiple threads to
execute each a smaller critical section. This may decrease contention on the
shared resource, and thus increase throughput of the total critical section.
Here is a very simplified Java code example to show what I mean:
public class TwoSums {
private int sum1 = 0;
private int sum2 = 0;
public void add(int val1, int val2){
synchronized(this){
this.sum1 += val1;
this.sum2 += val2;
}
}
}
Notice how the add() method adds values to two different sum member
variables. To prevent race conditions the summing is executed inside a Java
synchronized block. With this implementation only a single thread can ever
execute the summing at the same time.
However, since the two sum variables are independent of each other, you
could split their summing up into two separate synchronized blocks, like this:
public class TwoSums {
private int sum1 = 0;
private int sum2 = 0;
public void add(int val1, int val2){
synchronized(this){
this.sum1 += val1;
}
synchronized(this){
this.sum2 += val2;
}
}
}
Now two threads can execute the add() method at the same time. One thread
inside the first synchronized block, and another thread inside the second
synchronized block. This way threads will have to wait less for each other to
execute the add() method.
This example is very simple, of course. In a real life shared resource the
breaking down of critical sections may be a whole lot more complicated, and
require more analysis of execution order possibilities.
Thread Safety and shared Resources
Code that is safe to call by multiple threads simultaneously is called thread
safe. If a piece of code is thread safe, then it contains no race conditions.
Race condition only occur when multiple threads update shared resources.
Therefore it is important to know what resources Java threads share when
executing.
Local Variables
Local variables are stored in each thread's own stack. That means that local
variables are never shared between threads. That also means that all local
primitive variables are thread safe. Here is an example of a thread safe local
primitive variable:
public void someMethod(){
long threadSafeInt = 0;
threadSafeInt++;
}
Local Object References
Local references to objects are a bit different. The reference itself is not
shared. The object referenced however, is not stored in each threads's local
stack. All objects are stored in the shared heap.
If an object created locally never escapes the method it was created in, it is
thread safe. In fact you can also pass it on to other methods and objects as
long as none of these methods or objects make the passed object available to
other threads.
Here is an example of a thread safe local object:
public void someMethod(){
LocalObject localObject = new LocalObject();
localObject.callMethod();
method2(localObject);
}
public void method2(LocalObject localObject){
localObject.setValue("value");
}
The LocalObject instance in this example is not returned from the method, nor
is it passed to any other objects that are accessible from outside
the someMethod() method. Each thread executing the someMethod()method will
create its own LocalObject instance and assign it to the localObject reference.
Therefore the use of the LocalObject here is thread safe.
In fact, the whole method someMethod() is thread safe. Even if
the LocalObject instance is passed as parameter to other methods in the same
class, or in other classes, the use of it is thread safe.
The only exception is of course, if one of the methods called with
the LocalObject as parameter, stores theLocalObject instance in a way that
allows access to it from other threads.
Object Member Variables
Object member variables (fields) are stored on the heap along with the object.
Therefore, if two threads call a method on the same object instance and this
method updates object member variables, the method is not thread safe. Here
is an example of a method that is not thread safe:
public class NotThreadSafe{
StringBuilder builder = new StringBuilder();
public add(String text){
this.builder.append(text);
}
}
If two threads call the add() method simultaneously on the same
NotThreadSafe instance then it leads to race conditions. For instance:
NotThreadSafe sharedInstance = new NotThreadSafe();
new Thread(new MyRunnable(sharedInstance)).start();
new Thread(new MyRunnable(sharedInstance)).start();
public class MyRunnable implements Runnable{
NotThreadSafe instance = null;
public MyRunnable(NotThreadSafe instance){
this.instance = instance;
}
public void run(){
this.instance.add("some text");
}
}
Notice how the two MyRunnable instances share the
same NotThreadSafe instance. Therefore, when they call the add() method on
the NotThreadSafe instance it leads to race condition.
However, if two threads call the add() method simultaneously on different
instances then it does not lead to race condition. Here is the example from
before, but slightly modified:
new Thread(new MyRunnable(new NotThreadSafe())).start();
new Thread(new MyRunnable(new NotThreadSafe())).start();
Now the two threads have each their own instance of NotThreadSafe so their
calls to the add method doesn't interfere with each other. The code does not
have race condition anymore. So, even if an object is not thread safe it can
still be used in a way that doesn't lead to race condition.
The Thread Control Escape Rule
When trying to determine if your code's access of a certain resource is thread
safe you can use the thread control escape rule:
If a resource is created, used and disposed within
the control of the same thread,
and never escapes the control of this thread,
the use of that resource is thread safe.
Resources can be any shared resource like an object, array, file, database
connection, socket etc. In Java you do not always explicitly dispose objects,
so "disposed" means losing or null'ing the reference to the object.
Even if the use of an object is thread safe, if that object points to a shared
resource like a file or database, your application as a whole may not be thread
safe. For instance, if thread 1 and thread 2 each create their own database
connections, connection 1 and connection 2, the use of each connection itself
is thread safe. But the use of the database the connections point to may not
be thread safe. For example, if both threads execute code like this:
check if record X exists
if not, insert record X
If two threads execute this simultaneously, and the record X they are checking
for happens to be the same record, there is a risk that both of the threads end
up inserting it. This is how:
Thread 1 checks if record X exists. Result = no
Thread 2 checks if record X exists. Result = no
Thread 1 inserts record X
Thread 2 inserts record X
This could also happen with threads operating on files or other shared
resources. Therefore it is important to distinguish between whether an object
controlled by a thread is the resource, or if it merely referencesthe resource
(like a database connection does).
Synchronized method
A Java synchronized block marks a method or a block of code as
synchronized. Java synchronized blocks can be used to avoid race
conditions.
The Java synchronized Keyword
Synchronized blocks in Java are marked with the synchronized keyword. A
synchronized block in Java is synchronized on some object. All synchronized
blocks synchronized on the same object can only have one thread executing
inside them at the same time. All other threads attempting to enter the
synchronized block are blocked until the thread inside the synchronized block
exits the block.
The synchronized keyword can be used to mark four different types of blocks:
1. Instance methods
2. Static methods
3. Code blocks inside instance methods
4. Code blocks inside static methods
These blocks are synchronized on different objects. Which type of
synchronized block you need depends on the concrete situation.
Synchronized Instance Methods
Here is a synchronized instance method:
public synchronized void add(int value){
this.count += value;
}
Notice the use of the synchronized keyword in the method declaration. This
tells Java that the method is synchronized.
A synchronized instance method in Java is synchronized on the instance
(object) owning the method. Thus, each instance has its synchronized
methods synchronized on a different object: the owning instance. Only one
thread can execute inside a synchronized instance method. If more than one
instance exist, then one thread at a time can execute inside a synchronized
instance method per instance. One thread per instance.
Synchronized Static Methods
Static methods are marked as synchronized just like instance methods using
the synchronized keyword. Here is a Java synchronized static method
example:
public static synchronized void add(int value){
count += value;
}
Also here the synchronized keyword tells Java that the method is synchronized.
Synchronized static methods are synchronized on the class object of the class
the synchronized static method belongs to. Since only one class object exists
in the Java VM per class, only one thread can execute inside a static
synchronized method in the same class.
If the static synchronized methods are located in different classes, then one
thread can execute inside the static synchronized methods of each class. One
thread per class regardless of which static synchronized method it calls.
Synchronized Blocks in Instance Methods
You do not have to synchronize a whole method. Sometimes it is preferable to
synchronize only part of a method. Java synchronized blocks inside methods
makes this possible.
Here is a synchronized block of Java code inside an unsynchronized Java
method:
public void add(int value){
synchronized(this){
this.count += value;
}
}
This example uses the Java synchronized block construct to mark a block of
code as synchronized. This code will now execute as if it was a synchronized
method.
Notice how the Java synchronized block construct takes an object in
parentheses. In the example "this" is used, which is the instance the add
method is called on. The object taken in the parentheses by the synchronized
construct is called a monitor object. The code is said to be synchronized on
the monitor object. A synchronized instance method uses the object it belongs
to as monitor object.
Only one thread can execute inside a Java code block synchronized on the
same monitor object.
The following two examples are both synchronized on the instance they are
called on. They are therefore equivalent with respect to synchronization:
public class MyClass {
public synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public void log2(String msg1, String msg2){
synchronized(this){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
Thus only a single thread can execute inside either of the two synchronized
blocks in this example.
Had the second synchronized block been synchronized on a different object
than this, then one thread at a time had been able to execute inside each
method.
Synchronized Blocks in Static Methods
Here are the same two examples as static methods. These methods are
synchronized on the class object of the class the methods belong to:
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
Only one thread can execute inside any of these two methods at the same
time.
Had the second synchronized block been synchronized on a different object
than MyClass.class, then one thread could execute inside each method at the
same time.
Java Synchronized Example
Here is an example that starts 2 threads and have both of them call the add
method on the same instance of Counter. Only one thread at a time will be
able to call the add method on the same instance, because the method is
synchronized on the instance it belongs to.
public class Counter{
long count = 0;
public synchronized void add(long value){
this.count += value;
}
}
public class CounterThread extends Thread{
protected Counter counter = null;
public CounterThread(Counter counter){
this.counter = counter;
}
public void run() {
for(int i=0; i<10; i++){
counter.add(i);
}
}
}
public class Example {
public static void main(String[] args){
Counter counter = new Counter();
Thread threadA = new CounterThread(counter);
Thread threadB = new CounterThread(counter);
threadA.start();
threadB.start();
}
}
Two threads are created. The same Counter instance is passed to both of
them in their constructor. TheCounter.add() method is synchronized on the
instance, because the add method is an instance method, and marked as
synchronized. Therefore only one of the threads can call the add() method at
a time. The other thread will wait until the first thread leaves the add() method,
before it can execute the method itself.
If the two threads had referenced two separate Counter instances, there would
have been no problems calling the add() methods simultaneously. The calls
would have been to different objects, so the methods called would also be
synchronized on different objects (the object owning the method). Therefore
the calls would not block. Here is how that could look:
public class Example {
public static void main(String[] args){
Counter counterA = new Counter();
Counter counterB = new Counter();
Thread threadA = new CounterThread(counterA);
Thread threadB = new CounterThread(counterB);
threadA.start();
threadB.start();
}
}
Notice how the two threads, threadA and threadB, no longer reference the
same counter instance. The addmethod of counterA and counterB are
synchronized on their two owning instances. Calling add() on counterAwill thus
not block a call to add() on counterB.
Java Concurrency Utilities
The synchronized mechanism was Java's first mechanism for synchronizing
access to objects shared by multiple threads. The synchronized mechanism
isn't very advanced though. That is why Java 5 got a whole set
of concurrency utility classes to help developers implement more fine
grained concurrency control than what you get with synchronized.
Thread Deadlock
A deadlock is when two or more threads are blocked waiting to obtain locks
that some of the other threads in the deadlock are holding. Deadlock can
occur when multiple threads need the same locks, at the same time, but
obtain them in different order.
For instance, if thread 1 locks A, and tries to lock B, and thread 2 has already
locked B, and tries to lock A, a deadlock arises. Thread 1 can never get B,
and thread 2 can never get A. In addition, neither of them will ever know. They
will remain blocked on each their object, A and B, forever. This situation is a
deadlock.
The situation is illustrated below:
Thread 1 locks A, waits for B
Thread 2 locks B, waits for A
Here is an example of a TreeNode class that call synchronized methods in
different instances:
public class TreeNode {
TreeNode parent = null;
List children = new ArrayList();
public synchronized void addChild(TreeNode child){
if(!this.children.contains(child)) {
this.children.add(child);
child.setParentOnly(this);
}
}
public synchronized void addChildOnly(TreeNode child){
if(!this.children.contains(child){
this.children.add(child);
}
}
public synchronized void setParent(TreeNode parent){
this.parent = parent;
parent.addChildOnly(this);
}
public synchronized void setParentOnly(TreeNode parent){
this.parent = parent;
}
}
If a thread (1) calls the parent.addChild(child) method at the same time as
another thread (2) calls the child.setParent(parent) method, on the same
parent and child instances, a deadlock can occur. Here is some pseudo code
that illustrates this:
Thread 1: parent.addChild(child); //locks parent
--> child.setParentOnly(parent);
Thread 2: child.setParent(parent); //locks child
--> parent.addChildOnly()
First thread 1 calls parent.addChild(child). Since addChild() is synchronized
thread 1 effectively locks the parent object for access from other treads.
Then thread 2 calls child.setParent(parent). Since setParent() is synchronized
thread 2 effectively locks the child object for acces from other threads.
Now both child and parent objects are locked by two different threads. Next
thread 1 tries to call child.setParentOnly() method, but the child object is
locked by thread 2, so the method call just blocks. Thread 2 also tries to call
parent.addChildOnly() but the parent object is locked by thread 1, causing
thread 2 to block on that method call. Now both threads are blocked waiting to
obtain locks the other thread holds.
Note: The two threads must call parent.addChild(child) and
child.setParent(parent) at the same time as described above, and on the
same two parent and child instances for a deadlock to occur. The code above
may execute fine for a long time until all of a sudden it deadlocks.
The threads really need to take the locks *at the same time*. For instance, if
thread 1 is a bit ahead of thread2, and thus locks both A and B, then thread 2
will be blocked already when trying to lock B. Then no deadlock occurs. Since
thread scheduling often is unpredictable there is no way to predict *when* a
deadlock occurs. Only that it *can* occur.
More Complicated Deadlocks
Deadlock can also include more than two threads. This makes it harder to
detect. Here is an example in which four threads have deadlocked:
Thread 1 locks A, waits for B
Thread 2 locks B, waits for C
Thread 3 locks C, waits for D
Thread 4 locks D, waits for A
Thread 1 waits for thread 2, thread 2 waits for thread 3, thread 3 waits for
thread 4, and thread 4 waits for thread 1.
Database Deadlocks
A more complicated situation in which deadlocks can occur, is a database
transaction. A database transaction may consist of many SQL update
requests. When a record is updated during a transaction, that record is locked
for updates from other transactions, until the first transaction completes. Each
update request within the same transaction may therefore lock some records
in the database.
If multiple transactions are running at the same time that need to update the
same records, there is a risk of them ending up in a deadlock.
For example
Transaction 1, request 1, locks record 1 for update
Transaction 2, request 1, locks record 2 for update
Transaction 1, request 2, tries to lock record 2 for update.
Transaction 2, request 2, tries to lock record 1 for update.
Since the locks are taken in different requests, and not all locks needed for a
given transaction are known ahead of time, it is hard to detect or prevent
deadlocks in database transactions.
DEADLOOCK PREVENTION
In some situations it is possible to prevent deadlocks. I'll describe three
techniques in this text:
1. Lock Ordering
2. Lock Timeout
3. Deadlock Detection
Lock Ordering
Deadlock occurs when multiple threads need the same locks but obtain them
in different order.
If you make sure that all locks are always taken in the same order by any
thread, deadlocks cannot occur. Look at this example:
Thread 1:
lock A
lock B
Thread 2:
wait for A
lock C (when A locked)
Thread 3:
wait for A
wait for B
wait for C
If a thread, like Thread 3, needs several locks, it must take them in the
decided order. It cannot take a lock later in the sequence until it has obtained
the earlier locks.
For instance, neither Thread 2 or Thread 3 can lock C until they have locked A
first. Since Thread 1 holds lock A, Thread 2 and 3 must first wait until lock A is
unlocked. Then they must succeed in locking A, before they can attempt to
lock B or C.
Lock ordering is a simple yet effective deadlock prevention mechanism.
However, it can only be used if you know about all locks needed ahead of
taking any of the locks. This is not always the case.
Lock Timeout
Another deadlock prevention mechanism is to put a timeout on lock attempts
meaning a thread trying to obtain a lock will only try for so long before giving
up. If a thread does not succeed in taking all necessary locks within the given
timeout, it will backup, free all locks taken, wait for a random amount of time
and then retry. The random amount of time waited serves to give other
threads trying to take the same locks a chance to take all locks, and thus let
the application continue running without locking.
Here is an example of two threads trying to take the same two locks in
different order, where the threads back up and retry:
Thread 1 locks A
Thread 2 locks B
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.
In the above example Thread 2 will retry taking the locks about 200 millis
before Thread 1 and will therefore likely succeed at taking both locks. Thread
1 will then wait already trying to take lock A. When Thread 2 finishes, Thread
1 will be able to take both locks too (unless Thread 2 or another thread takes
the locks in between).
An issue to keep in mind is, that just because a lock times out it does not
necessarily mean that the threads had deadlocked. It could also just mean
that the thread holding the lock (causing the other thread to time out) takes a
long time to complete its task.
Additionally, if enough threads compete for the same resources they still risk
trying to take the threads at the same time again and again, even if timing out
and backing up. This may not occur with 2 threads each waiting between 0
and 500 millis before retrying, but with 10 or 20 threads the situation is
different. Then the likeliness of two threads waiting the same time before
retrying (or close enough to cause problems) is a lot higher.
A problem with the lock timeout mechanism is that it is not possible to set a
timeout for entering a synchronized block in Java. You will have to create a
custom lock class or use one of the Java 5 concurrency constructs in the
java.util.concurrency package. Writing custom locks isn't difficult but it is
outside the scope of this text. Later texts in the Java concurrency trails will
cover custom locks.
Deadlock Detection
Deadlock detection is a heavier deadlock prevention mechanism aimed at
cases in which lock ordering isn't possible, and lock timeout isn't feasible.
Every time a thread takes a lock it is noted in a data structure (map, graph
etc.) of threads and locks. Additionally, whenever a thread requests a lock
this is also noted in this data structure.
When a thread requests a lock but the request is denied, the thread can
traverse the lock graph to check for deadlocks. For instance, if a Thread A
requests lock 7, but lock 7 is held by Thread B, then Thread A can check if
Thread B has requested any of the locks Thread A holds (if any). If Thread B
has requested so, a deadlock has occurred (Thread A having taken lock 1,
requesting lock 7, Thread B having taken lock 7, requesting lock 1).
Of course a deadlock scenario may be a lot more complicated than two
threads holding each others locks. Thread A may wait for Thread B, Thread B
waits for Thread C, Thread C waits for Thread D, and Thread D waits for
Thread A. In order for Thread A to detect a deadlock it must transitively
examine all requested locks by Thread B. From Thread B's requested locks
Thread A will get to Thread C, and then to Thread D, from which it finds one of
the locks Thread A itself is holding. Then it knows a deadlock has occurred.
Below is a graph of locks taken and requested by 4 threads (A, B, C and D). A
data structure like this that can be used to detect deadlocks.
So what do the threads do if a deadlock is detected?
One possible action is to release all locks, backup, wait a random amount of
time and then retry. This is similar to the simpler lock timeout mechanism
except threads only backup when a deadlock has actually occurred. Not just
because their lock requests timed out. However, if a lot of threads are
competing for the same locks they may repeatedly end up in a deadlock even
if they back up and wait.
A better option is to determine or assign a priority of the threads so that only
one (or a few) thread backs up. The rest of the threads continue taking the
locks they need as if no deadlock had occurred. If the priority assigned to the
threads is fixed, the same threads will always be given higher priority. To
avoid this you may assign the priority randomly whenever a deadlock is
detected.
Causes of Starvation in Java
The following three common causes can lead to starvation of threads in Java:
1. Threads with high priority swallow all CPU time from threads with lower
priority.
2. Threads are blocked indefinately waiting to enter a synchronized block,
because other threads are constantly allowed access before it.
3. Threads waiting on an object (called wait() on it) remain waiting
indefinitely because other threads are constantly awakened instead of it.
Threads with high priority swallow all CPU time from threads with lower priority
You can set the thread priority of each thread individually. The higher the
priority the more CPU time the thread is granted. You can set the priority of
threads between 1 and 10. Exactly how this is interpreted depends on the
operating system your application is running on. For most applications you are
better off leaving the priority unchanged.
Threads are blocked indefinitely waiting to enter a synchronized block
Java's synchronized code blocks can be another cause of starvation. Java's
synchronized code block makes no guarantee about the sequence in which
threads waiting to enter the synchronized block are allowed to enter. This
means that there is a theoretical risk that a thread remains blocked forever
trying to enter the block, because other threads are constantly granted access
before it. This problem is called "starvation", that a thread is "starved to death"
by because other threads are allowed the CPU time instead of it.
Threads waiting on an object (called wait() on it) remain waiting indefinitely
The notify() method makes no guarantee about what thread is awakened if
multiple thread have called wait() on the object notify() is called on. It could be
any of the threads waiting. Therefore there is a risk that a thread waiting on a
certain object is never awakened because other waiting threads are always
awakened instead of it.
Implementing Fairness in Java
While it is not possible to implement 100% fairness in Java we can still
implement our synchronization constructs to increase fairness between
threads.
First lets study a simple synchronized code block:
public class Synchronizer{
public synchronized void doSynchronized(){
//do a lot of work which takes a long time
}
}
If more than one thread call the doSynchronized() method, some of them will
be blocked until the first thread granted access has left the method. If more
than one thread are blocked waiting for access there is no guarantee about
which thread is granted access next.
Using Locks Instead of Synchronized Blocks
To increase the fairness of waiting threads first we will change the code block
to be guarded by a lock rather than a synchronized block:
public class Synchronizer{
Lock lock = new Lock();
public void doSynchronized() throws InterruptedException{
this.lock.lock();
//critical section, do a lot of work which takes a long time
this.lock.unlock();
}
}
Notice how the doSynchronized() method is no longer declared synchronized.
Instead the critical section is guarded by the lock.lock() and lock.unlock() calls.
A simple implementation of the Lock class could look like this:
public class Lock{
private boolean isLocked = false;
private Thread lockingThread = null;
public synchronized void lock() throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
lockingThread = Thread.currentThread();
}
public synchronized void unlock(){
if(this.lockingThread != Thread.currentThread()){
throw new IllegalMonitorStateException(
"Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
notify();
}
}
If you look at the Synchronizer class above and look into this Lock
implementation you will notice that threads are now blocked trying to access
the lock() method, if more than one thread calls lock() simultanously. Second,
if the lock is locked, the threads are blocked in the wait() call inside the
while(isLocked) loop in the lock() method. Remember that a thread calling
wait() releases the synchronization lock on the Lock instance, so threads
waiting to enter lock() can now do so. The result is that multiple threads can
end up having called wait() inside lock().
If you look back at the doSynchronized() method you will notice that the
comment between lock() and unlock() states, that the code in between these
two calls take a "long" time to execute. Let us further assume that this code
takes long time to execute compared to entering the lock() method and calling
wait() because the lock is locked. This means that the majority of the time
waited to be able to lock the lock and enter the critical section is spent waiting
in the wait() call inside the lock() method, not being blocked trying to enter the
lock() method.
As stated earlier synchronized blocks makes no guarantees about what
thread is being granted access if more than one thread is waiting to enter. Nor
does wait() make any guarantees about what thread is awakened when
notify() is called. So, the current version of the Lock class makes no different
guarantees with respect to fairness than synchronized version of
doSynchronized(). But we can change that.
The current version of the Lock class calls its own wait() method. If instead
each thread calls wait() on a separate object, so that only one thread has
called wait() on each object, the Lock class can decide which of these objects
to call notify() on, thereby effectively selecting exactly what thread to awaken.
A Fair Lock
Below is shown the previous Lock class turned into a fair lock called FairLock.
You will notice that the implementation has changed a bit with respect to
synchronization and wait() / notify() compared to the Lock class shown
earlier.
Exactly how I arrived at this design beginning from the previous Lock class is
a longer story involving several incremental design steps, each fixing the
problem of the previous step: Nested Monitor Lockout,Slipped Conditions,
and Missed Signals. That discussion is left out of this text to keep the text
short, but each of the steps are discussed in the appropriate texts on the topic
( see the links above). What is important is, that every thread calling lock() is
now queued, and only the first thread in the queue is allowed to lock the
FairLock instance, if it is unlocked. All other threads are parked waiting until
they reach the top of the queue.
public class FairLock {
private boolean isLocked = false;
private Thread lockingThread = null;
private List<QueueObject> waitingThreads =
new ArrayList<QueueObject>();
public void lock() throws InterruptedException{
QueueObject queueObject = new QueueObject();
boolean isLockedForThisThread = true;
synchronized(this){
waitingThreads.add(queueObject);
}
while(isLockedForThisThread){
synchronized(this){
isLockedForThisThread =
isLocked || waitingThreads.get(0) != queueObject;
if(!isLockedForThisThread){
isLocked = true;
waitingThreads.remove(queueObject);
lockingThread = Thread.currentThread();
return;
}
}
try{
queueObject.doWait();
}catch(InterruptedException e){
synchronized(this) { waitingThreads.remove(queueObject); }
throw e;
}
}
}
public synchronized void unlock(){
if(this.lockingThread != Thread.currentThread()){
throw new IllegalMonitorStateException(
"Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
if(waitingThreads.size() > 0){
waitingThreads.get(0).doNotify();
}
}
}
public class QueueObject {
private boolean isNotified = false;
public synchronized void doWait() throws InterruptedException {
while(!isNotified){
this.wait();
}
this.isNotified = false;
}
public synchronized void doNotify() {
this.isNotified = true;
this.notify();
}
public boolean equals(Object o) {
return this == o;
}
}
First you might notice that the lock() method is no longer
declared synchronized. Instead only the blocks necessary to synchronize are
nested inside synchronized blocks.
FairLock creates a new instance of QueueObject and enqueue it for each thread
calling lock(). The thread calling unlock() will take the top QueueObject in the
queue and call doNotify() on it, to awaken the thread waiting on that object.
This way only one waiting thread is awakened at a time, rather than all waiting
threads. This part is what governs the fairness of the FairLock.
Notice how the state of the lock is still tested and set within the same
synchronized block to avoid slipped conditions.
Also notice that the QueueObject is really a semaphore.
The doWait() and doNotify() methods store the signal internally in
the QueueObject. This is done to avoid missed signals caused by a thread being
preempted just before calling queueObject.doWait(), by another thread which
calls unlock() and therebyqueueObject.doNotify(). The queueObject.doWait() call
is placed outside the synchronized(this) block to avoid nested monitor lockout,
so another thread can actually call unlock() when no thread is executing inside
the synchronized(this) block in lock() method.
Finally, notice how the queueObject.doWait() is called inside a try -
catch block. In case an InterruptedException is thrown the thread leaves the
lock() method, and we need to dequeue it.
A Note on Performance
If you compare the Lock and FairLock classes you will notice that there is
somewhat more going on inside the lock() and unlock() in the FairLock class.
This extra code will cause the FairLock to be a sligtly slower synchronization
mechanism than Lock. How much impact this will have on your application
depends on how long time the code in the critical section guarded by
the FairLock takes to execute. The longer this takes to execute, the less
significant the added overhead of the synchronizer is. It does of course also
depend on how often this code is called.
Thanks
Regards Saquib Khan
Concurrency in java

More Related Content

What's hot (6)

PPTX
Clustered PHP - DC PHP 2009
marcelesser
 
PPTX
Threads and multi threading
Antonio Cesarano
 
DOC
PARALLEL ARCHITECTURE AND COMPUTING - SHORT NOTES
suthi
 
PPTX
Threads in JAVA
Haldia Institute of Technology
 
PPT
Processes and Threads in Windows Vista
Trinh Phuc Tho
 
PDF
Vmreport
meru2ks
 
Clustered PHP - DC PHP 2009
marcelesser
 
Threads and multi threading
Antonio Cesarano
 
PARALLEL ARCHITECTURE AND COMPUTING - SHORT NOTES
suthi
 
Processes and Threads in Windows Vista
Trinh Phuc Tho
 
Vmreport
meru2ks
 

Similar to Concurrency in java (20)

PPT
multithreading
Rajkattamuri
 
PPT
Java
mdfkhan625
 
PPT
Multithreading
F K
 
PPT
Java Multithreading
Rajkattamuri
 
PPT
Java multithreading
Mohammed625
 
PPT
Java
Khasim Cise
 
PPT
JAVA MULTITHREDED PROGRAMMING - LECTURES
rm170484
 
DOCX
Threadnotes
Himanshu Rajput
 
PPTX
Multithreading in java
Raghu nath
 
PPT
Md09 multithreading
Rakesh Madugula
 
PPT
Java Performance, Threading and Concurrent Data Structures
Hitendra Kumar
 
PPTX
U4 JAVA.pptx
madan r
 
PPT
multhi threading concept in oops through java
Parameshwar Maddela
 
PDF
Unit-3 MULTITHREADING-2.pdf
GouthamSoma1
 
PPTX
Chap3 multi threaded programming
raksharao
 
PPT
web programming-Multithreading concept in Java.ppt
mcjaya2024
 
PPT
Advanced Java Programming for Beginners.
rm170484
 
PDF
Java thread life cycle
Archana Gopinath
 
PPTX
Java Thread & Multithreading
jehan1987
 
PPTX
Multithreading in java
Kavitha713564
 
multithreading
Rajkattamuri
 
Multithreading
F K
 
Java Multithreading
Rajkattamuri
 
Java multithreading
Mohammed625
 
JAVA MULTITHREDED PROGRAMMING - LECTURES
rm170484
 
Threadnotes
Himanshu Rajput
 
Multithreading in java
Raghu nath
 
Md09 multithreading
Rakesh Madugula
 
Java Performance, Threading and Concurrent Data Structures
Hitendra Kumar
 
U4 JAVA.pptx
madan r
 
multhi threading concept in oops through java
Parameshwar Maddela
 
Unit-3 MULTITHREADING-2.pdf
GouthamSoma1
 
Chap3 multi threaded programming
raksharao
 
web programming-Multithreading concept in Java.ppt
mcjaya2024
 
Advanced Java Programming for Beginners.
rm170484
 
Java thread life cycle
Archana Gopinath
 
Java Thread & Multithreading
jehan1987
 
Multithreading in java
Kavitha713564
 
Ad

Recently uploaded (20)

PDF
IDM Crack with Internet Download Manager 6.42 Build 43 with Patch Latest 2025
bashirkhan333g
 
PPTX
Help for Correlations in IBM SPSS Statistics.pptx
Version 1 Analytics
 
PDF
IObit Driver Booster Pro 12.4.0.585 Crack Free Download
henryc1122g
 
PDF
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
PPTX
AEM User Group: India Chapter Kickoff Meeting
jennaf3
 
PDF
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
PDF
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
PPTX
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
PPTX
In From the Cold: Open Source as Part of Mainstream Software Asset Management
Shane Coughlan
 
PPTX
Homogeneity of Variance Test Options IBM SPSS Statistics Version 31.pptx
Version 1 Analytics
 
PDF
AOMEI Partition Assistant Crack 10.8.2 + WinPE Free Downlaod New Version 2025
bashirkhan333g
 
PDF
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
PPTX
Agentic Automation Journey Session 1/5: Context Grounding and Autopilot for E...
klpathrudu
 
PDF
Build It, Buy It, or Already Got It? Make Smarter Martech Decisions
bbedford2
 
PDF
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
PPTX
Empowering Asian Contributions: The Rise of Regional User Groups in Open Sour...
Shane Coughlan
 
PDF
유니티에서 Burst Compiler+ThreadedJobs+SIMD 적용사례
Seongdae Kim
 
PDF
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
PPTX
OpenChain @ OSS NA - In From the Cold: Open Source as Part of Mainstream Soft...
Shane Coughlan
 
PPTX
ChiSquare Procedure in IBM SPSS Statistics Version 31.pptx
Version 1 Analytics
 
IDM Crack with Internet Download Manager 6.42 Build 43 with Patch Latest 2025
bashirkhan333g
 
Help for Correlations in IBM SPSS Statistics.pptx
Version 1 Analytics
 
IObit Driver Booster Pro 12.4.0.585 Crack Free Download
henryc1122g
 
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
AEM User Group: India Chapter Kickoff Meeting
jennaf3
 
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
In From the Cold: Open Source as Part of Mainstream Software Asset Management
Shane Coughlan
 
Homogeneity of Variance Test Options IBM SPSS Statistics Version 31.pptx
Version 1 Analytics
 
AOMEI Partition Assistant Crack 10.8.2 + WinPE Free Downlaod New Version 2025
bashirkhan333g
 
MiniTool Partition Wizard 12.8 Crack License Key LATEST
hashhshs786
 
Agentic Automation Journey Session 1/5: Context Grounding and Autopilot for E...
klpathrudu
 
Build It, Buy It, or Already Got It? Make Smarter Martech Decisions
bbedford2
 
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
Empowering Asian Contributions: The Rise of Regional User Groups in Open Sour...
Shane Coughlan
 
유니티에서 Burst Compiler+ThreadedJobs+SIMD 적용사례
Seongdae Kim
 
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
OpenChain @ OSS NA - In From the Cold: Open Source as Part of Mainstream Soft...
Shane Coughlan
 
ChiSquare Procedure in IBM SPSS Statistics Version 31.pptx
Version 1 Analytics
 
Ad

Concurrency in java

  • 1. Concurrency in Java- Multi Threading Back in the old days a computer had a single CPU, and was only capable of executing a single program at a time. Later came multitasking which meant that computers could execute multiple programs (AKA tasks or processes) at the same time. It wasn't really "at the same time" though. The single CPU was shared between the programs. The operating system would switch between the programs running, executing each of them for a little while before switching. Along with multitasking came new challenges for software developers. Programs can no longer assume to have all the CPU time available, nor all memory or any other computer resources. A "good citizen" program should release all resources it is no longer using, so other programs can use them. Later yet came multithreading which mean that you could have multiple threads of execution inside the same program. A thread of execution can be thought of as a CPU executing the program. When you have multiple threads executing the same program, it is like having multiple CPUs execute within the same program. Multithreading can be a great way to increase the performance of some types of programs. However, mulithreading is even more challenging than multitasking. The threads are executing within the same program and are hence reading and writing the same memory simultanously. This can result in errors not seen in a singlethreaded program. Some of these errors may not be seen on single CPU machines, because two threads never really execute "simultanously". Modern computers, though, come with multi core CPUs, and even with multiple CPUs too. This means that separate threads can be executed by separate cores or CPUs simultanously.
  • 2. If a thread reads a memory location while another thread writes to it, what value will the first thread end up reading? The old value? The value written by the second thread? Or a value that is a mix between the two? Or, if two threads are writing to the same memory location simultanously, what value will be left when they are done? The value written by the first thread? The value written by the second thread? Or a mix of the two values written? Without proper precautions any of these outcomes are possible. The behaviour would not even be predictable. The outcome could change from time to time. Therefore it is important as a developer to know how to take the right precautions - meaning learning to control how threads access shared resources like memory, files, databases etc. That is one of the topics this Java concurrency tutorial addresses. Multithreading and Concurrency in Java Java was one of the first languages to make multithreading easily available to developers. Java had multithreading capabilities from the very beginning. Therefore, Java developers often face the problems described above. That is the reason I am writing this trail on Java concurrency. As notes to myself, and any fellow Java developer whom may benefit from it. The trail will primarily be concerned with multithreading in Java, but some of the problems occurring in multithreading are similar to problems occurring in multitasking and in distributed systems. References to multitasking and
  • 3. distributed systems may therefore occur in this trail too. Hence the word "concurrency" rather than "multithreading". MultiThreading Benefits The reason multithreading is still used in spite of its challenges is that multithreading can have several benefits. Some of these benefits are:  Better resource utilization.  Simpler program design in some situations.  More responsive programs. Better resource utilization Imagine an application that reads and processes files from the local file system. Lets say that reading af file from disk takes 5 seconds and processing it takes 2 seconds. Processing two files then takes 5 seconds reading file A 2 seconds processing file A 5 seconds reading file B 2 seconds processing file B ----------------------- 14 seconds total When reading the file from disk most of the CPU time is spent waiting for the disk to read the data. The CPU is pretty much idle during that time. It could be doing something else. By changing the order of the operations, the CPU could be better utilized. Look at this ordering: 5 seconds reading file A 5 seconds reading file B + 2 seconds processing file A 2 seconds processing file B
  • 4. ----------------------- 12 seconds total The CPU waits for the first file to be read. Then it starts the read of the second file. While the second file is being read, the CPU processes the first file. Remember, while waiting for the file to be read from disk, the CPU is mostly idle. In general, the CPU can be doing other things while waiting for IO. It doesn't have to be disk IO. It can be network IO as well, or input from a user at the machine. Network and disk IO is often a lot slower than CPU's and memory IO. Simpler Program Design If you were to program the above ordering of reading and processing by hand in a singlethreaded application, you would have to keep track of both the read and processing state of each file. Instead you can start two threads that each just reads and processes a single file. Each of these threads will be blocked while waiting for the disk to read its file. While waiting, other threads can use the CPU to process the parts of the file they have already read. The result is, that the disk is kept busy at all times, reading from various files into memory. This results in a better utilization of both the disk and the CPU. It is also easier to program, since each thread only has to keep track of a single file. More responsive programs Another common goal for turning a singlethreaded application into a multithreaded application is to achieve a more responsive application. Imagine a server application that listens on some port for incoming requests. when a request is received, it handles the request and then goes back to listening. The server loop is sketched below: while(server is active){ listen for request process request }
  • 5. If the request takes a long time to process, no new clients can send requests to the server for that duration. Only while the server is listening can requests be received. An alternate design would be for the listening thread to pass the request to a worker thread, and return to listening immediatedly. The worker thread will process the request and send a reply to the client. This design is sketched below: while(server is active){ listen for request hand request to worker thread } This way the server thread will be back at listening sooner. Thus more clients can send requests to the server. The server has become more responsive. The same is true for desktop applications. If you click a button that starts a long task, and the thread executing the task is the thread updating the windows, buttons etc., then the application will appear unresponsive while the task executes. Instead the task can be handed off to a worker thread. While the worker thread is busy with the task, the window thread is free to respond to other user requests. When the worker thread is done it signals the window thread. The window thread can then update the application windows with the result of the task. The program with the worker thread design will appear more responsive to the user.
  • 6. Creating and Starting Threads Creating a thread in Java is done like this: Thread thread = new Thread(); To start the Java thread you will call its start() method, like this: thread.start(); This example doesn't specify any code for the thread to execute. The thread will stop again right away after it is started. There are two ways to specify what code the thread should execute. The first is to create a subclass of Thread and override the run() method. The second method is to pass an object that implements Runnable(java.lang.Runnable to the Thread constructor. Both methods are covered below. Thread Subclass The first way to specify what code a thread is to run, is to create a subclass of Thread and override therun() method. The run() method is what is executed by the thread after you call start(). Here is an example of creating a Java Thread subclass: public class MyThread extends Thread { public void run(){ System.out.println("MyThread running"); } } To create and start the above thread you can do like this: MyThread myThread = new MyThread(); myTread.start(); The start() call will return as soon as the thread is started. It will not wait until the run() method is done. The run() method will execute as if executed by a
  • 7. different CPU. When the run() method executes it will print out the text "MyThread running". You can also create an anonymous subclass of Thread like this: Thread thread = new Thread(){ public void run(){ System.out.println("Thread Running"); } } thread.start(); This example will print out the text "Thread running" once the run() method is executed by the new thread. Runnable Interface Implementation The second way to specify what code a thread should run is by creating a class that implementsjava.lang.Runnable. The Runnable object can be executed by a Thread. Here is a Java Runnable example: public class MyRunnable implements Runnable { public void run(){ System.out.println("MyRunnable running"); } } To have the run() method executed by a thread, pass an instance of MyRunnable to a Thread in its constructor. Here is how that is done: Thread thread = new Thread(new MyRunnable()); thread.start(); When the thread is started it will call the run() method of the MyRunnable instance instead of executing it's own run() method. The above example would print out the text "MyRunnable running". You can also create an anonymous implementation of Runnable, like this:
  • 8. Runnable myRunnable = new Runnable(){ public void run(){ System.out.println("Runnable running"); } } Thread thread = new Thread(myRunnable); thread.start(); Subclass or Runnable? There are no rules about which of the two methods that is the best. Both methods works. Personally though, I prefer implementing Runnable, and handing an instance of the implementation to a Threadinstance. When having the Runnable's executed by a thread pool it is easy to queue up the Runnableinstances until a thread from the pool is idle. This is a little harder to do with Thread subclasses. Sometimes you may have to implement Runnable as well as subclass Thread. For instance, if creating a subclass of Thread that can execute more than one Runnable. This is typically the case when implementing a thread pool. Common Pitfall: Calling run() Instead of start() When creating and starting a thread a common mistake is to call the run() method of the Thread instead ofstart(), like this: Thread newThread = new Thread(MyRunnable()); newThread.run(); //should be start(); At first you may not notice anything because the Runnable's run() method is executed like you expected. However, it is NOT executed by the new thread you just created. Instead the run() method is executed by the thread that created the thread. In other words, the thread that executed the above two lines of code. To have the run() method of the MyRunnable instance called by the new created thread, newThread, you MUST call the newThread.start() method. Thread Names
  • 9. When you create a Java thread you can give it a name. The name can help you distinguish different threads from each other. For instance, if multiple threads write to System.out it can be handy to see which thread wrote the text. Here is an example: Thread thread = new Thread("New Thread") { public void run(){ System.out.println("run by: " + getName()); } }; thread.start(); System.out.println(thread.getName()); Notice the string "New Thread" passed as parameter to the Thread constructor. This string is the name of the thread. The name can be obtained via the Thread's getName() method. You can also pass a name to aThread when using a Runnable implementation. Here is how that looks: MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable, "New Thread"); thread.start(); System.out.println(thread.getName()); Notice however, that since the MyRunnable class is not a subclass of Thread, it does not have access to thegetName() method of the thread executing it. Thread.currentThread() The Thread.currentThread() method returns a reference to the Thread instance executing currentThread() . This way you can get access to the Java Thread object representing the thread executing a given block of code. Here is an example of how to use Thread.currentThread() : Thread thread = Thread.currentThread(); Once you have a reference to the Thread object, you can call methods on it. For instance, you can get the name of the thread currently executing the code like this:
  • 10. String threadName = Thread.currentThread().getName(); Java Thread Example Here is a small example. First it prints out the name of the thread executing the main() method. This thread is assigned by the JVM. Then it starts up 10 threads and give them all a number as name ("" + i). Each thread then prints its name out, and then stops executing. public class ThreadExample { public static void main(String[] args){ System.out.println(Thread.currentThread().getName()); for(int i=0; i<10; i++){ new Thread("" + i){ public void run(){ System.out.println("Thread: " + getName() + " running"); } }.start(); } } } Note that even if the threads are started in sequence (1, 2, 3 etc.) they may not execute sequentially, meaning thread 1 may not be the first thread to write its name to System.out. This is because the threads are in principle executing in parallel and not sequentially. The JVM and/or operating system determines the order in which the threads are executed. This order does not have to be the same order in which they were started. Critical Section and Race Condition A race condition is a special condition that may occur inside a critical section. A critical section is a section of code that is executed by multiple threads and where the sequence of execution for the threads makes a difference in the result of the concurrent execution of the critical section.
  • 11. When the result of multiple threads executing a critical section may differ depending on the sequence in which the threads execute, the critical section is said to contain a race condition. The term race condition stems from the metaphor that the threads are racing through the critical section, and that the result of that race impacts the result of executing the critical section. This may all sound a bit complicated, so I will elaborate more on race conditions and critical sections in the following sections. Critical Sections Running more than one thread inside the same application does not by itself cause problems. The problems arise when multiple threads access the same resources. For instance the same memory (variables, arrays, or objects), systems (databases, web services etc.) or files. In fact, problems only arise if one or more of the threads write to these resources. It is safe to let multiple threads read the same resources, as long as the resources do not change. Here is a critical section Java code example that may fail if executed by multiple threads simultaneously: public class Counter { protected long count = 0; public void add(long value){ this.count = this.count + value; } } Imagine if two threads, A and B, are executing the add method on the same instance of the Counter class. There is no way to know when the operating system switches between the two threads. The code in theadd() method is not executed as a single atomic instruction by the Java virtual machine. Rather it is executed as a set of smaller instructions, similar to this: 1. Read this.count from memory into register. 2. Add value to register. 3. Write register to memory. Observe what happens with the following mixed execution of threads A and B:
  • 12. this.count = 0; A: Reads this.count into a register (0) B: Reads this.count into a register (0) B: Adds value 2 to register B: Writes register value (2) back to memory. this.count now equals 2 A: Adds value 3 to register A: Writes register value (3) back to memory. this.count now equals 3 The two threads wanted to add the values 2 and 3 to the counter. Thus the value should have been 5 after the two threads complete execution. However, since the execution of the two threads is interleaved, the result ends up being different. In the execution sequence example listed above, both threads read the value 0 from memory. Then they add their i ndividual values, 2 and 3, to the value, and write the result back to memory. Instead of 5, the value left in this.count will be the value written by the last thread to write its value. In the above case it is thread A, but it could as well have been thread B. Race Conditions in Critical Sections The code in the add() method in the example earlier contains a critical section. When multiple threads execute this critical section, race conditions occur. More formally, the situation where two threads compete for the same resource, where the sequence in which the resource is accessed is significant, is called race conditions. A code section that leads to race conditions is called a critical section. Preventing Race Conditions To prevent race conditions from occurring you must make sure that the critical section is executed as an atomic instruction. That means that once a single thread is executing it, no other threads can execute it until the first thread has left the critical section. Race conditions can be avoided by proper thread synchronization in critical sections. Thread synchronization can be achieved using a synchronized block of Java code. Thread synchronization can also be achieved using other synchronization constructs like locks or atomic variables likejava.util.concurrent.atomic.AtomicInteger.
  • 13. Critical Section Throughput For smaller critical sections making the whole critical section a synchronized block may work. But, for larger critical sections it may be beneficial to break the critical section into smaller critical sections, to allow multiple threads to execute each a smaller critical section. This may decrease contention on the shared resource, and thus increase throughput of the total critical section. Here is a very simplified Java code example to show what I mean: public class TwoSums { private int sum1 = 0; private int sum2 = 0; public void add(int val1, int val2){ synchronized(this){ this.sum1 += val1; this.sum2 += val2; } } } Notice how the add() method adds values to two different sum member variables. To prevent race conditions the summing is executed inside a Java synchronized block. With this implementation only a single thread can ever execute the summing at the same time. However, since the two sum variables are independent of each other, you could split their summing up into two separate synchronized blocks, like this: public class TwoSums { private int sum1 = 0; private int sum2 = 0; public void add(int val1, int val2){ synchronized(this){ this.sum1 += val1; } synchronized(this){ this.sum2 += val2; } } }
  • 14. Now two threads can execute the add() method at the same time. One thread inside the first synchronized block, and another thread inside the second synchronized block. This way threads will have to wait less for each other to execute the add() method. This example is very simple, of course. In a real life shared resource the breaking down of critical sections may be a whole lot more complicated, and require more analysis of execution order possibilities. Thread Safety and shared Resources Code that is safe to call by multiple threads simultaneously is called thread safe. If a piece of code is thread safe, then it contains no race conditions. Race condition only occur when multiple threads update shared resources. Therefore it is important to know what resources Java threads share when executing. Local Variables Local variables are stored in each thread's own stack. That means that local variables are never shared between threads. That also means that all local primitive variables are thread safe. Here is an example of a thread safe local primitive variable: public void someMethod(){ long threadSafeInt = 0; threadSafeInt++; } Local Object References
  • 15. Local references to objects are a bit different. The reference itself is not shared. The object referenced however, is not stored in each threads's local stack. All objects are stored in the shared heap. If an object created locally never escapes the method it was created in, it is thread safe. In fact you can also pass it on to other methods and objects as long as none of these methods or objects make the passed object available to other threads. Here is an example of a thread safe local object: public void someMethod(){ LocalObject localObject = new LocalObject(); localObject.callMethod(); method2(localObject); } public void method2(LocalObject localObject){ localObject.setValue("value"); } The LocalObject instance in this example is not returned from the method, nor is it passed to any other objects that are accessible from outside the someMethod() method. Each thread executing the someMethod()method will create its own LocalObject instance and assign it to the localObject reference. Therefore the use of the LocalObject here is thread safe. In fact, the whole method someMethod() is thread safe. Even if the LocalObject instance is passed as parameter to other methods in the same class, or in other classes, the use of it is thread safe. The only exception is of course, if one of the methods called with the LocalObject as parameter, stores theLocalObject instance in a way that allows access to it from other threads. Object Member Variables Object member variables (fields) are stored on the heap along with the object. Therefore, if two threads call a method on the same object instance and this method updates object member variables, the method is not thread safe. Here is an example of a method that is not thread safe:
  • 16. public class NotThreadSafe{ StringBuilder builder = new StringBuilder(); public add(String text){ this.builder.append(text); } } If two threads call the add() method simultaneously on the same NotThreadSafe instance then it leads to race conditions. For instance: NotThreadSafe sharedInstance = new NotThreadSafe(); new Thread(new MyRunnable(sharedInstance)).start(); new Thread(new MyRunnable(sharedInstance)).start(); public class MyRunnable implements Runnable{ NotThreadSafe instance = null; public MyRunnable(NotThreadSafe instance){ this.instance = instance; } public void run(){ this.instance.add("some text"); } } Notice how the two MyRunnable instances share the same NotThreadSafe instance. Therefore, when they call the add() method on the NotThreadSafe instance it leads to race condition. However, if two threads call the add() method simultaneously on different instances then it does not lead to race condition. Here is the example from before, but slightly modified: new Thread(new MyRunnable(new NotThreadSafe())).start(); new Thread(new MyRunnable(new NotThreadSafe())).start(); Now the two threads have each their own instance of NotThreadSafe so their calls to the add method doesn't interfere with each other. The code does not have race condition anymore. So, even if an object is not thread safe it can still be used in a way that doesn't lead to race condition.
  • 17. The Thread Control Escape Rule When trying to determine if your code's access of a certain resource is thread safe you can use the thread control escape rule: If a resource is created, used and disposed within the control of the same thread, and never escapes the control of this thread, the use of that resource is thread safe. Resources can be any shared resource like an object, array, file, database connection, socket etc. In Java you do not always explicitly dispose objects, so "disposed" means losing or null'ing the reference to the object. Even if the use of an object is thread safe, if that object points to a shared resource like a file or database, your application as a whole may not be thread safe. For instance, if thread 1 and thread 2 each create their own database connections, connection 1 and connection 2, the use of each connection itself is thread safe. But the use of the database the connections point to may not be thread safe. For example, if both threads execute code like this: check if record X exists if not, insert record X If two threads execute this simultaneously, and the record X they are checking for happens to be the same record, there is a risk that both of the threads end up inserting it. This is how: Thread 1 checks if record X exists. Result = no Thread 2 checks if record X exists. Result = no Thread 1 inserts record X Thread 2 inserts record X This could also happen with threads operating on files or other shared resources. Therefore it is important to distinguish between whether an object controlled by a thread is the resource, or if it merely referencesthe resource (like a database connection does).
  • 18. Synchronized method A Java synchronized block marks a method or a block of code as synchronized. Java synchronized blocks can be used to avoid race conditions. The Java synchronized Keyword Synchronized blocks in Java are marked with the synchronized keyword. A synchronized block in Java is synchronized on some object. All synchronized blocks synchronized on the same object can only have one thread executing inside them at the same time. All other threads attempting to enter the synchronized block are blocked until the thread inside the synchronized block exits the block. The synchronized keyword can be used to mark four different types of blocks: 1. Instance methods 2. Static methods 3. Code blocks inside instance methods 4. Code blocks inside static methods These blocks are synchronized on different objects. Which type of synchronized block you need depends on the concrete situation. Synchronized Instance Methods Here is a synchronized instance method: public synchronized void add(int value){ this.count += value; } Notice the use of the synchronized keyword in the method declaration. This tells Java that the method is synchronized. A synchronized instance method in Java is synchronized on the instance (object) owning the method. Thus, each instance has its synchronized methods synchronized on a different object: the owning instance. Only one thread can execute inside a synchronized instance method. If more than one
  • 19. instance exist, then one thread at a time can execute inside a synchronized instance method per instance. One thread per instance. Synchronized Static Methods Static methods are marked as synchronized just like instance methods using the synchronized keyword. Here is a Java synchronized static method example: public static synchronized void add(int value){ count += value; } Also here the synchronized keyword tells Java that the method is synchronized. Synchronized static methods are synchronized on the class object of the class the synchronized static method belongs to. Since only one class object exists in the Java VM per class, only one thread can execute inside a static synchronized method in the same class. If the static synchronized methods are located in different classes, then one thread can execute inside the static synchronized methods of each class. One thread per class regardless of which static synchronized method it calls. Synchronized Blocks in Instance Methods You do not have to synchronize a whole method. Sometimes it is preferable to synchronize only part of a method. Java synchronized blocks inside methods makes this possible. Here is a synchronized block of Java code inside an unsynchronized Java method: public void add(int value){ synchronized(this){ this.count += value; } }
  • 20. This example uses the Java synchronized block construct to mark a block of code as synchronized. This code will now execute as if it was a synchronized method. Notice how the Java synchronized block construct takes an object in parentheses. In the example "this" is used, which is the instance the add method is called on. The object taken in the parentheses by the synchronized construct is called a monitor object. The code is said to be synchronized on the monitor object. A synchronized instance method uses the object it belongs to as monitor object. Only one thread can execute inside a Java code block synchronized on the same monitor object. The following two examples are both synchronized on the instance they are called on. They are therefore equivalent with respect to synchronization: public class MyClass { public synchronized void log1(String msg1, String msg2){ log.writeln(msg1); log.writeln(msg2); } public void log2(String msg1, String msg2){ synchronized(this){ log.writeln(msg1); log.writeln(msg2); } } } Thus only a single thread can execute inside either of the two synchronized blocks in this example. Had the second synchronized block been synchronized on a different object than this, then one thread at a time had been able to execute inside each method. Synchronized Blocks in Static Methods Here are the same two examples as static methods. These methods are synchronized on the class object of the class the methods belong to:
  • 21. public class MyClass { public static synchronized void log1(String msg1, String msg2){ log.writeln(msg1); log.writeln(msg2); } public static void log2(String msg1, String msg2){ synchronized(MyClass.class){ log.writeln(msg1); log.writeln(msg2); } } } Only one thread can execute inside any of these two methods at the same time. Had the second synchronized block been synchronized on a different object than MyClass.class, then one thread could execute inside each method at the same time. Java Synchronized Example Here is an example that starts 2 threads and have both of them call the add method on the same instance of Counter. Only one thread at a time will be able to call the add method on the same instance, because the method is synchronized on the instance it belongs to. public class Counter{ long count = 0; public synchronized void add(long value){ this.count += value; } } public class CounterThread extends Thread{ protected Counter counter = null; public CounterThread(Counter counter){ this.counter = counter; } public void run() { for(int i=0; i<10; i++){ counter.add(i);
  • 22. } } } public class Example { public static void main(String[] args){ Counter counter = new Counter(); Thread threadA = new CounterThread(counter); Thread threadB = new CounterThread(counter); threadA.start(); threadB.start(); } } Two threads are created. The same Counter instance is passed to both of them in their constructor. TheCounter.add() method is synchronized on the instance, because the add method is an instance method, and marked as synchronized. Therefore only one of the threads can call the add() method at a time. The other thread will wait until the first thread leaves the add() method, before it can execute the method itself. If the two threads had referenced two separate Counter instances, there would have been no problems calling the add() methods simultaneously. The calls would have been to different objects, so the methods called would also be synchronized on different objects (the object owning the method). Therefore the calls would not block. Here is how that could look: public class Example { public static void main(String[] args){ Counter counterA = new Counter(); Counter counterB = new Counter(); Thread threadA = new CounterThread(counterA); Thread threadB = new CounterThread(counterB); threadA.start(); threadB.start(); } } Notice how the two threads, threadA and threadB, no longer reference the same counter instance. The addmethod of counterA and counterB are synchronized on their two owning instances. Calling add() on counterAwill thus not block a call to add() on counterB. Java Concurrency Utilities
  • 23. The synchronized mechanism was Java's first mechanism for synchronizing access to objects shared by multiple threads. The synchronized mechanism isn't very advanced though. That is why Java 5 got a whole set of concurrency utility classes to help developers implement more fine grained concurrency control than what you get with synchronized. Thread Deadlock A deadlock is when two or more threads are blocked waiting to obtain locks that some of the other threads in the deadlock are holding. Deadlock can occur when multiple threads need the same locks, at the same time, but obtain them in different order. For instance, if thread 1 locks A, and tries to lock B, and thread 2 has already locked B, and tries to lock A, a deadlock arises. Thread 1 can never get B, and thread 2 can never get A. In addition, neither of them will ever know. They will remain blocked on each their object, A and B, forever. This situation is a deadlock. The situation is illustrated below: Thread 1 locks A, waits for B Thread 2 locks B, waits for A Here is an example of a TreeNode class that call synchronized methods in different instances: public class TreeNode { TreeNode parent = null; List children = new ArrayList(); public synchronized void addChild(TreeNode child){ if(!this.children.contains(child)) { this.children.add(child); child.setParentOnly(this);
  • 24. } } public synchronized void addChildOnly(TreeNode child){ if(!this.children.contains(child){ this.children.add(child); } } public synchronized void setParent(TreeNode parent){ this.parent = parent; parent.addChildOnly(this); } public synchronized void setParentOnly(TreeNode parent){ this.parent = parent; } } If a thread (1) calls the parent.addChild(child) method at the same time as another thread (2) calls the child.setParent(parent) method, on the same parent and child instances, a deadlock can occur. Here is some pseudo code that illustrates this: Thread 1: parent.addChild(child); //locks parent --> child.setParentOnly(parent); Thread 2: child.setParent(parent); //locks child --> parent.addChildOnly() First thread 1 calls parent.addChild(child). Since addChild() is synchronized thread 1 effectively locks the parent object for access from other treads. Then thread 2 calls child.setParent(parent). Since setParent() is synchronized thread 2 effectively locks the child object for acces from other threads. Now both child and parent objects are locked by two different threads. Next thread 1 tries to call child.setParentOnly() method, but the child object is locked by thread 2, so the method call just blocks. Thread 2 also tries to call parent.addChildOnly() but the parent object is locked by thread 1, causing thread 2 to block on that method call. Now both threads are blocked waiting to obtain locks the other thread holds. Note: The two threads must call parent.addChild(child) and child.setParent(parent) at the same time as described above, and on the
  • 25. same two parent and child instances for a deadlock to occur. The code above may execute fine for a long time until all of a sudden it deadlocks. The threads really need to take the locks *at the same time*. For instance, if thread 1 is a bit ahead of thread2, and thus locks both A and B, then thread 2 will be blocked already when trying to lock B. Then no deadlock occurs. Since thread scheduling often is unpredictable there is no way to predict *when* a deadlock occurs. Only that it *can* occur. More Complicated Deadlocks Deadlock can also include more than two threads. This makes it harder to detect. Here is an example in which four threads have deadlocked: Thread 1 locks A, waits for B Thread 2 locks B, waits for C Thread 3 locks C, waits for D Thread 4 locks D, waits for A Thread 1 waits for thread 2, thread 2 waits for thread 3, thread 3 waits for thread 4, and thread 4 waits for thread 1. Database Deadlocks A more complicated situation in which deadlocks can occur, is a database transaction. A database transaction may consist of many SQL update requests. When a record is updated during a transaction, that record is locked for updates from other transactions, until the first transaction completes. Each update request within the same transaction may therefore lock some records in the database. If multiple transactions are running at the same time that need to update the same records, there is a risk of them ending up in a deadlock. For example Transaction 1, request 1, locks record 1 for update Transaction 2, request 1, locks record 2 for update Transaction 1, request 2, tries to lock record 2 for update.
  • 26. Transaction 2, request 2, tries to lock record 1 for update. Since the locks are taken in different requests, and not all locks needed for a given transaction are known ahead of time, it is hard to detect or prevent deadlocks in database transactions. DEADLOOCK PREVENTION In some situations it is possible to prevent deadlocks. I'll describe three techniques in this text: 1. Lock Ordering 2. Lock Timeout 3. Deadlock Detection Lock Ordering Deadlock occurs when multiple threads need the same locks but obtain them in different order. If you make sure that all locks are always taken in the same order by any thread, deadlocks cannot occur. Look at this example: Thread 1: lock A lock B Thread 2: wait for A lock C (when A locked) Thread 3: wait for A wait for B wait for C If a thread, like Thread 3, needs several locks, it must take them in the decided order. It cannot take a lock later in the sequence until it has obtained the earlier locks.
  • 27. For instance, neither Thread 2 or Thread 3 can lock C until they have locked A first. Since Thread 1 holds lock A, Thread 2 and 3 must first wait until lock A is unlocked. Then they must succeed in locking A, before they can attempt to lock B or C. Lock ordering is a simple yet effective deadlock prevention mechanism. However, it can only be used if you know about all locks needed ahead of taking any of the locks. This is not always the case. Lock Timeout Another deadlock prevention mechanism is to put a timeout on lock attempts meaning a thread trying to obtain a lock will only try for so long before giving up. If a thread does not succeed in taking all necessary locks within the given timeout, it will backup, free all locks taken, wait for a random amount of time and then retry. The random amount of time waited serves to give other threads trying to take the same locks a chance to take all locks, and thus let the application continue running without locking. Here is an example of two threads trying to take the same two locks in different order, where the threads back up and retry: Thread 1 locks A Thread 2 locks B Thread 1 attempts to lock B but is blocked Thread 2 attempts to lock A but is blocked Thread 1's lock attempt on B times out Thread 1 backs up and releases A as well Thread 1 waits randomly (e.g. 257 millis) before retrying. Thread 2's lock attempt on A times out Thread 2 backs up and releases B as well Thread 2 waits randomly (e.g. 43 millis) before retrying. In the above example Thread 2 will retry taking the locks about 200 millis before Thread 1 and will therefore likely succeed at taking both locks. Thread 1 will then wait already trying to take lock A. When Thread 2 finishes, Thread 1 will be able to take both locks too (unless Thread 2 or another thread takes the locks in between). An issue to keep in mind is, that just because a lock times out it does not necessarily mean that the threads had deadlocked. It could also just mean
  • 28. that the thread holding the lock (causing the other thread to time out) takes a long time to complete its task. Additionally, if enough threads compete for the same resources they still risk trying to take the threads at the same time again and again, even if timing out and backing up. This may not occur with 2 threads each waiting between 0 and 500 millis before retrying, but with 10 or 20 threads the situation is different. Then the likeliness of two threads waiting the same time before retrying (or close enough to cause problems) is a lot higher. A problem with the lock timeout mechanism is that it is not possible to set a timeout for entering a synchronized block in Java. You will have to create a custom lock class or use one of the Java 5 concurrency constructs in the java.util.concurrency package. Writing custom locks isn't difficult but it is outside the scope of this text. Later texts in the Java concurrency trails will cover custom locks. Deadlock Detection Deadlock detection is a heavier deadlock prevention mechanism aimed at cases in which lock ordering isn't possible, and lock timeout isn't feasible. Every time a thread takes a lock it is noted in a data structure (map, graph etc.) of threads and locks. Additionally, whenever a thread requests a lock this is also noted in this data structure. When a thread requests a lock but the request is denied, the thread can traverse the lock graph to check for deadlocks. For instance, if a Thread A requests lock 7, but lock 7 is held by Thread B, then Thread A can check if Thread B has requested any of the locks Thread A holds (if any). If Thread B has requested so, a deadlock has occurred (Thread A having taken lock 1, requesting lock 7, Thread B having taken lock 7, requesting lock 1). Of course a deadlock scenario may be a lot more complicated than two threads holding each others locks. Thread A may wait for Thread B, Thread B waits for Thread C, Thread C waits for Thread D, and Thread D waits for Thread A. In order for Thread A to detect a deadlock it must transitively examine all requested locks by Thread B. From Thread B's requested locks Thread A will get to Thread C, and then to Thread D, from which it finds one of the locks Thread A itself is holding. Then it knows a deadlock has occurred.
  • 29. Below is a graph of locks taken and requested by 4 threads (A, B, C and D). A data structure like this that can be used to detect deadlocks. So what do the threads do if a deadlock is detected? One possible action is to release all locks, backup, wait a random amount of time and then retry. This is similar to the simpler lock timeout mechanism except threads only backup when a deadlock has actually occurred. Not just because their lock requests timed out. However, if a lot of threads are competing for the same locks they may repeatedly end up in a deadlock even if they back up and wait. A better option is to determine or assign a priority of the threads so that only one (or a few) thread backs up. The rest of the threads continue taking the locks they need as if no deadlock had occurred. If the priority assigned to the threads is fixed, the same threads will always be given higher priority. To
  • 30. avoid this you may assign the priority randomly whenever a deadlock is detected. Causes of Starvation in Java The following three common causes can lead to starvation of threads in Java: 1. Threads with high priority swallow all CPU time from threads with lower priority. 2. Threads are blocked indefinately waiting to enter a synchronized block, because other threads are constantly allowed access before it. 3. Threads waiting on an object (called wait() on it) remain waiting indefinitely because other threads are constantly awakened instead of it. Threads with high priority swallow all CPU time from threads with lower priority You can set the thread priority of each thread individually. The higher the priority the more CPU time the thread is granted. You can set the priority of threads between 1 and 10. Exactly how this is interpreted depends on the operating system your application is running on. For most applications you are better off leaving the priority unchanged. Threads are blocked indefinitely waiting to enter a synchronized block Java's synchronized code blocks can be another cause of starvation. Java's synchronized code block makes no guarantee about the sequence in which threads waiting to enter the synchronized block are allowed to enter. This means that there is a theoretical risk that a thread remains blocked forever trying to enter the block, because other threads are constantly granted access before it. This problem is called "starvation", that a thread is "starved to death" by because other threads are allowed the CPU time instead of it. Threads waiting on an object (called wait() on it) remain waiting indefinitely The notify() method makes no guarantee about what thread is awakened if multiple thread have called wait() on the object notify() is called on. It could be any of the threads waiting. Therefore there is a risk that a thread waiting on a
  • 31. certain object is never awakened because other waiting threads are always awakened instead of it. Implementing Fairness in Java While it is not possible to implement 100% fairness in Java we can still implement our synchronization constructs to increase fairness between threads. First lets study a simple synchronized code block: public class Synchronizer{ public synchronized void doSynchronized(){ //do a lot of work which takes a long time } } If more than one thread call the doSynchronized() method, some of them will be blocked until the first thread granted access has left the method. If more than one thread are blocked waiting for access there is no guarantee about which thread is granted access next. Using Locks Instead of Synchronized Blocks To increase the fairness of waiting threads first we will change the code block to be guarded by a lock rather than a synchronized block: public class Synchronizer{ Lock lock = new Lock(); public void doSynchronized() throws InterruptedException{ this.lock.lock(); //critical section, do a lot of work which takes a long time this.lock.unlock(); } } Notice how the doSynchronized() method is no longer declared synchronized. Instead the critical section is guarded by the lock.lock() and lock.unlock() calls. A simple implementation of the Lock class could look like this:
  • 32. public class Lock{ private boolean isLocked = false; private Thread lockingThread = null; public synchronized void lock() throws InterruptedException{ while(isLocked){ wait(); } isLocked = true; lockingThread = Thread.currentThread(); } public synchronized void unlock(){ if(this.lockingThread != Thread.currentThread()){ throw new IllegalMonitorStateException( "Calling thread has not locked this lock"); } isLocked = false; lockingThread = null; notify(); } } If you look at the Synchronizer class above and look into this Lock implementation you will notice that threads are now blocked trying to access the lock() method, if more than one thread calls lock() simultanously. Second, if the lock is locked, the threads are blocked in the wait() call inside the while(isLocked) loop in the lock() method. Remember that a thread calling wait() releases the synchronization lock on the Lock instance, so threads waiting to enter lock() can now do so. The result is that multiple threads can end up having called wait() inside lock(). If you look back at the doSynchronized() method you will notice that the comment between lock() and unlock() states, that the code in between these two calls take a "long" time to execute. Let us further assume that this code takes long time to execute compared to entering the lock() method and calling wait() because the lock is locked. This means that the majority of the time waited to be able to lock the lock and enter the critical section is spent waiting in the wait() call inside the lock() method, not being blocked trying to enter the lock() method. As stated earlier synchronized blocks makes no guarantees about what thread is being granted access if more than one thread is waiting to enter. Nor does wait() make any guarantees about what thread is awakened when notify() is called. So, the current version of the Lock class makes no different
  • 33. guarantees with respect to fairness than synchronized version of doSynchronized(). But we can change that. The current version of the Lock class calls its own wait() method. If instead each thread calls wait() on a separate object, so that only one thread has called wait() on each object, the Lock class can decide which of these objects to call notify() on, thereby effectively selecting exactly what thread to awaken. A Fair Lock Below is shown the previous Lock class turned into a fair lock called FairLock. You will notice that the implementation has changed a bit with respect to synchronization and wait() / notify() compared to the Lock class shown earlier. Exactly how I arrived at this design beginning from the previous Lock class is a longer story involving several incremental design steps, each fixing the problem of the previous step: Nested Monitor Lockout,Slipped Conditions, and Missed Signals. That discussion is left out of this text to keep the text short, but each of the steps are discussed in the appropriate texts on the topic ( see the links above). What is important is, that every thread calling lock() is now queued, and only the first thread in the queue is allowed to lock the FairLock instance, if it is unlocked. All other threads are parked waiting until they reach the top of the queue. public class FairLock { private boolean isLocked = false; private Thread lockingThread = null; private List<QueueObject> waitingThreads = new ArrayList<QueueObject>(); public void lock() throws InterruptedException{ QueueObject queueObject = new QueueObject(); boolean isLockedForThisThread = true; synchronized(this){ waitingThreads.add(queueObject); } while(isLockedForThisThread){ synchronized(this){ isLockedForThisThread = isLocked || waitingThreads.get(0) != queueObject; if(!isLockedForThisThread){ isLocked = true; waitingThreads.remove(queueObject); lockingThread = Thread.currentThread(); return;
  • 34. } } try{ queueObject.doWait(); }catch(InterruptedException e){ synchronized(this) { waitingThreads.remove(queueObject); } throw e; } } } public synchronized void unlock(){ if(this.lockingThread != Thread.currentThread()){ throw new IllegalMonitorStateException( "Calling thread has not locked this lock"); } isLocked = false; lockingThread = null; if(waitingThreads.size() > 0){ waitingThreads.get(0).doNotify(); } } } public class QueueObject { private boolean isNotified = false; public synchronized void doWait() throws InterruptedException { while(!isNotified){ this.wait(); } this.isNotified = false; } public synchronized void doNotify() { this.isNotified = true; this.notify(); } public boolean equals(Object o) { return this == o; } } First you might notice that the lock() method is no longer declared synchronized. Instead only the blocks necessary to synchronize are nested inside synchronized blocks. FairLock creates a new instance of QueueObject and enqueue it for each thread calling lock(). The thread calling unlock() will take the top QueueObject in the queue and call doNotify() on it, to awaken the thread waiting on that object.
  • 35. This way only one waiting thread is awakened at a time, rather than all waiting threads. This part is what governs the fairness of the FairLock. Notice how the state of the lock is still tested and set within the same synchronized block to avoid slipped conditions. Also notice that the QueueObject is really a semaphore. The doWait() and doNotify() methods store the signal internally in the QueueObject. This is done to avoid missed signals caused by a thread being preempted just before calling queueObject.doWait(), by another thread which calls unlock() and therebyqueueObject.doNotify(). The queueObject.doWait() call is placed outside the synchronized(this) block to avoid nested monitor lockout, so another thread can actually call unlock() when no thread is executing inside the synchronized(this) block in lock() method. Finally, notice how the queueObject.doWait() is called inside a try - catch block. In case an InterruptedException is thrown the thread leaves the lock() method, and we need to dequeue it. A Note on Performance If you compare the Lock and FairLock classes you will notice that there is somewhat more going on inside the lock() and unlock() in the FairLock class. This extra code will cause the FairLock to be a sligtly slower synchronization mechanism than Lock. How much impact this will have on your application depends on how long time the code in the critical section guarded by the FairLock takes to execute. The longer this takes to execute, the less significant the added overhead of the synchronizer is. It does of course also depend on how often this code is called. Thanks Regards Saquib Khan