Java Multithreading Execution Order
Java Multithreading Execution Order
Types of errors
exceptions,
try & catch statement,
nested try statement,
throws & Finally statement,
build-in exceptions,
chained exceptions,
Creating own exception, subclasses.
4.2 Multithreaded Programming
Introduction
Rarely does a program run successfully at its very first attempt. It is common to make
mistakes while developing as well as typing a program. a mistake might lead to an
error causing the program to produce unexpected results. Errors are the wrongs that
can make program go wrong.
An error may produce an incorrect output or may terminate the execution of the
program abruptly or even may cause the system to crash. It is therefore important to
detect and manage properly all the possible error conditions in the program so that the
program will not terminate or crash during execution.
Types of errors
1. Compile-time errors
2. Run-time errors
1. Compile-Time Errors
All syntax errors will be detected and displayed by the Java compiler and therefore
these errors are known as compile-time errors. Whenever the computer displays an
error, it will not create .class file. It is therefore necessary that we fix all the errors
before we can successfully compile and run the program.
2. Run-Time Errors
Sometimes, a program may compile successfully creating the .class file but may not
run properly. Such programs may produce wrong results due to wrong logic or may
terminate due to errors such as stack overflow. Such a errors are called as Run-Time
errors. Run-Time errors are detected and display by Java Interpreter.
When such errors are encountered, Java typically generates an error message and
aborts the programs.
Types of Exception
There are mainly two types of exceptions: checked and unchecked where error is
considered as unchecked exception. The sun microsystem says there are
three types of exceptions:
1. Checked Exception
2. Unchecked Exception
3. Error
The classes that extend Throwable class except RuntimeException and Error
are known as checked exceptions [Link], SQLException etc. Checked
exceptions are checked at compile-time.
2) Unchecked Exception
The classes that extend RuntimeException are known as unchecked exceptions
e.g. ArithmeticException, NullPointerException,
ArrayIndexOutOfBoundsException etc.
Unchecked exceptions are not checked at compile-time rather they are checked at
runtime.
3) Error
Error is irrecoverable
e.g. OutOfMemoryError, VirtualMachineError, AssertionError etc.
There are given some scenarios where unchecked exceptions can occur. They are as
follows:
int a=50/0;//ArithmeticException
2)Scenario where NullPointerException occurs
If we have null value in any variable, performing any operation by the variable occurs
an NullPointerException.
String s=null;
[Link]([Link]()); //NullPointerException
String s="abc";
int i=[Link](s);//NumberFormatException
If you are inserting any value in the wrong index, it would result
ArrayIndexOutOfBoundsException as shown below:
Exceptions
If we want the program to continue with the execution of the remaining code, then we
should try to catch the exception object thrown by the error condition and then
displaying an appropriate message for taking corrective actions. This task is known as
exception handling.
Exception Hierarchy
Java uses a keyword try to preface a block of code that is likely to cause an error
condition and “throw” an exception. A catch block defined by the keyword catch
“catches” the exception “thrown” by the try block and handles it appropriately. The
catch block is added immediately after the try block.
SYNTAX:
try
{
Statement; //generates an exception
}
catch (Exception – type1 e)
{
Statement; //processes the exception
}
class Testtrycatch1
{
public static void main(String args[])
{
int data=50/0; //may throw exception
[Link]("rest of the code...");
}
}
Output:
There can be 100 lines of code after exception. So all the code after exception will not be
executed.
class Testtrycatch2
{
public static void main(String args[])
{
try
{
int data=50/0;
}catch(ArithmeticException e)
{
[Link](e);
}
[Link]("rest of the code...");
}
}
Output:
Now, as displayed in the above example, rest of the code is executed i.e. rest of
the code... statement is printed.
In some cases, more than one exception could be raised by a single piece of code. To
handle this type of situation, you can specify two or more catch clauses, each catching
a different type of exception. When an exception is thrown, each catch statement is
inspected in order, and the first one whose type matches that of the exception is
executed. After one catch statement executes, the others are bypassed, and execution
continues after the try/catch block.
SYNTAX:
try
{
Statement; //generates an exception
}
catch (Exception – type1 e)
{
Statement; //processes the exception
}
catch (Exception – type2 e)
{
Statement; //processes the exception
}
………………
catch (Exception – typeN e)
{
Statement; //processes the exception
}
……………….
The try statement can be nested. That is, a try statement can be used inside the block
of another try. Each time when a try statement is entered, the context of that exception
is pushed on the stack. If an inner try statement does not have a catch handler for a
particular exception, the stack is unwound and the next try statement‟s catch handlers
are inspected for a match of exception. This continues until one of the catch
statements succeeds, or until all the nested try statements are exhausted. If no catch
statement matches, then the Java run-time system will handle the exception.
try
{
z=82/x; //statement1
[Link] ("Division: "+z);
try
{
int a = 100 / (x-1); //statement2
short arr [] = {15};
arr [10] =25; //statement3
[Link]("Inner try end...");
}
catch (ArrayIndexOutOfBoundsException e) //1
{
[Link]("Array indexing wrong");
}
[Link]("Outer try end...");
}
catch (ArithmeticException e) //2
{
[Link]("Division by zero");
}
[Link]("Program end...");
}
}
The finally is a block of code after try/catch block that will be executed after a
try/catch block has completed and before the code following the try/catch block. The
finally block will execute whether an exception is thrown or not. If an exception is
thrown, the finally block will execute even if no catch statement matches the
exception.
If any time a method is about to return to the caller from inside a try/catch block, via
an uncaught exception or an explicit return statement, the finally clause is also
executed just before the method returns. This can be useful for closing file handles
and freeing up any other resources that might have been allocated at the beginning of
a method. The finally clause is optional. But, each try statement requires at least one
catch or a finally clause. The try-catch-finally of try-finally construct can be created
as,
try
{
.......
}
catch (....) //multiple catch are allowed
{ ....... }
finally
{
.......
}
or
try
{
.......
}
finally
{
.......
}
If a method is capable of causing an exception that it does not handle, it must specify
this behaviour so that callers of the method can protect themselves against that
exception. We do this by including a throws clause in front of the method‟s
declaration.
A throws clause lists the types of exceptions that a method might throw. This is
necessary for all exceptions, except those of type Error or RuntimeException, or any
of their subclasses. All other exceptions that a method can throw must be declared in
the throws clause. If they are not, a compile-time error will occur. The general form
of writing a throws clause is,
Here the exception-list is the list of exceptions that the method might throw separated
by comma.
For many classes and methods, it is necessary to throw the exception by the method
which is using it. For example, while using DataInputStream it is necessary to throw
the IOException as well as for many methods of multithreading it is necessary to
throw InterruptedException.
class ThrowsDemo
{
public static void main (String args []) throws ClassNotFoundException
{
//statements
}
}
Java has defined a lot of Exception and Error classes for different conditions. But
many times it is required in some conditions that we want to create your own
exception types to handle situations specific to our applications.
This is quite easy to do. We just have to define a subclass of Exception (which is a
subclass of Throwable). Our subclasses don‟t need to actually implement anything.
It is their existence in the type system that allows us to use them as exceptions. The
Exception class does not define any methods of its own. It inherits all the methods
provided by class Throwable.
Thus, all exceptions, including those that we create, have the methods defined by
Throwable available to them. In order to create our own exception, we need to derive
our class from Exception.
If we want to throw the exceptions by our own, the throw statement can be used. That
is, it will force the Java run-time system to throw an exception. The general form of
throw statement is:
class OwnException
{
public static void main (String args [])
{
int x = [Link](args[0]);
int y = [Link](args[1]);
int z;
try
{
z = x * y;
if(z<0) //statement1
throw new NegativeOutputException(z);
[Link]("Output: "+z);
}
catch (NegativeOutputException e)
{
[Link]("Caught: "+e);
}
}
}
Outputs:
java OwnException 4 8
Output: 32
java OwnException 4 -3
Caught: NegativeOutputException[-12]
java OwnException -4 -3
Output: 12
Example 2
class TestMyException
{
public static void main (String args [])
{
int x = 5, y = 1000;
try
{
float z = (float) x / (float) y;
if (z < 0.01)
{
throw new MyException (“Number is too small”);
}
}
catch (MyException e)
{
[Link](“Caught my exception”);
[Link](e);
}
finally
{
[Link](“I am always here”);
}
}
}
throw throws
Throw keyword is used to Java throws Java throws keyword is used to declare an
keyword is used to explicitly throw an exception.
exception.
Checked exception cannot be propagated using Checked exception can be propagated with
throw only. throws.
Throw is followed by an instance. Throws is followed by class.
Throw is used within the method. Throws is used with the method signature.
You cannot throw multiple exceptions. You can declare multiple exceptions e.g.
public void method()throws
IOException,SQLException.
Multithreading is one of the features of Java. It provides built-in support for multithreaded
programming. Basically, it is not supported by most of the programming languages.
A multithreaded program contains two or more parts that can run concurrently. Each part of
such a program is called a thread, and each thread defines a separate path of execution.
That is a thread is a light-weight process. We can call multithreading is a specialized form of
multitasking.
The thread is the smallest unit of dispatchable code. This means that a single program can
perform two or more tasks simultaneously. A thread is similar to a program that has single
flow of control.
Multithreading Multitasking
It is a programming concept In which a It is an operating system concept in which
program or a process is divided into two or multiple tasks are performed simultaneously.
more subprograms or threads that are executed
at the same time in parallel.
It supports execution of multiple parts of a It supports execution of multiple programs
single program simultaneously. simultaneously.
The processor has to switch between different The processor has to switch between different
parts or threads of a program. programs or processes.
It is highly efficient. It is less efficient in comparison to
multithreading.
A thread is the smallest unit in multithreading A program or process is the smallest unit in a
multitasking environment.
Creating a Thread:
Creating threads in Java is simple. Thread are implemented in the form of objects that
contain a method called run (). The run () method is the heart and soul of any thread.
It makes up the entire body of a thread and is the only method in which the thread‟s
behaviour can be implemented. A typical run () would appear as follows:
The run () method should be invoked by an object of the concerned thread. This can
be achieved by creating the thread and initiating it with the help of another thread
method called start ().
1. By creating a thread class: Define a class that extends Thread class and override its run
() method with code required by the thread.
2. By converting a class to a thread: Define a class that implements Runnable interface.
The Runnable interface has only one method, run (), that is to be defined in the method
with code to be executed by the thread.
Extending the Thread class
To actually create and run an instance of our thread class, we must write the following:
There are many threads in which a thread can enter during its lifetime. These are:
1. Newborn state
2. Runnable state
3. Running state
4. Blocked state
5. Dead state
A thread is always in one of these five states. It can be shifted from one state to
another via variety of ways as shown in figure.
Newborn state
After creation of the thread object, the thread is born which is called as new-born
thread. This thread is not scheduled for running. We can either start this state to make
it runnable using start () method or kill the thread using stop () method. Only these
two operations can be performed on this state of the thread. If we attempt to perform
any other operation, the exception will be thrown.
Runnable state
When the thread is ready for execution and is waiting for availability of the processor,
it is said to be in runnable state. The thread has joined the queue of threads that are
waiting for execution. If all the threads have equal priority, then they are given time
slots for execution in round robin fashion i.e. on first come first serve basis.
The thread that relinquishes control joins the queue at the end and again waits for its
execution. This process of assigning time to threads is known as time slicing. If we
want a thread to relinquish control to another thread of equal priority, a yield ()
method can be used.
Running state
When the processor has given time to the thread for its execution then the thread is
said to be in running state. The thread runs until it relinquishes control to its own or it
is pre-empted by a higher priority thread. A running thread may relinquish its control
in any one of the following situations:
1. The thread has been suspended using suspend () method. This can be revived by
using resume () method. This is useful when we want to suspend a thread for some
time rather than killing it.
2. We can put a thread to sleep using sleep (time) method where „time‟ is the time
value given in milliseconds. Means, the thread is out of queue during this time period.
3. A thread can wait until some event occurs using wait () method. This thread can be
scheduled to run again using notify () method.
Blocked state
When a thread is prevented from entering into the runnable state and subsequently in
running state it is said to be blocked. This happens when the thread is suspended,
sleeping or waiting in order to satisfy certain requirements. A blocked thread is
considered “not runnable” but not dead and therefore fully qualified to run again.
Dead state
A running thread ends its life when it has completed its execution of run () method. It
is a natural death. However, we can kill it by sending the stop message to it at any
state thus causing a premature death to it. A thread can be killed as soon as it is born
or while it is running or even when it is in blocked state.
Thread exceptions
For example, a sleeping thread cannot deal with the resume () method because a
sleeping thread cannot receive any instructions. The same is true with suspend ()
method when it is used on a blocked thread.
Whenever we call a thread method that is likely to throw an exception, we have to
supply an appropriate exception handler to catch it. The catch statement may take one
of the following forms:
catch (ThreadDeath e)
{
………….. //Killed Thread
…………..
}
catch (InterruptedException e)
{
…………………… //Cannot handle it in the current state
}
catch (IllegalArgumentException e)
{
……………………. //Illegal method argument
}
catch (Exception e)
{
………………………. //Any other
}
Thread priorities are used by the thread scheduler to decide when each thread should
be allowed to run.
In theory, over a given period of time, higher-priority threads get more CPU time than
lower-priority threads.
In practice, the amount of CPU time that a thread gets often depends on several
factors besides its priority. (For example, how an operating system implements
multitasking can affect the relative availability of CPU time.) A higher-priority thread
can also preempt a lower-priority one.
Here, level specifies the new priority setting for the calling thread. The value of level
must be within the range MIN_PRIORITY and MAX_PRIORITY.
Currently, these values are 1 and 10, respectively.
Synchronization
When two or more threads need access to a shared resource, they need some way to
ensure that the resource will be used by only one thread at a time. The process by
which this is achieved is called synchronization.
Key to synchronization is the concept of the monitor (also called a semaphore). A
monitor is an object that is used as a mutually exclusive lock, or mutex. Only one
thread can own a monitor at a given time. When a thread acquires a lock, it is said to
have entered the monitor. All other threads attempting to enter the locked monitor will
be suspended until the first thread exits the monitor. These other threads are said to be
waiting for the monitor. A thread that owns a monitor can reenter the same monitor if
it so desires.
Synchronization is easy in Java, because all objects have their own implicit monitor
associated with them. To enter an object‟s monitor, just call a method that has been
modified with the synchronized keyword. While a thread is inside a synchronized
method, all other threads that try to call it (or any other synchronized method) on the
same instance have to wait. To exit the monitor and relinquish control of the object to
the next waiting thread, the owner of the monitor simply returns from the
synchronized method.
[Link]();
}
public void run() {
[Link](msg);
}
}
class Synch {
public static void main(String args[]) {
Callme target = new Callme();
Caller ob1 = new Caller(target, "Hello");
Caller ob2 = new Caller(target, "Synchronized");
Caller ob3 = new Caller(target, "World");
// wait for threads to end
try {
[Link]();
[Link]();
[Link]();
} catch(InterruptedException e) {
[Link]("Interrupted");
}
}
}
Hello[Synchronized[World]
]
]
As you can see, by calling sleep( ), the call( ) method allows execution to switch to
another thread. This results in the mixed-up output of the three message strings. In
this program, nothing exists to stop all three threads from calling the same method, on
the same object, at the same time. This is known as a race condition, because the three
threads are racing each other to complete the method.
To fix the preceding program, you must serialize access to call( ). That is, you must
restrict its access to only one thread at a time. To do this, you simply need to precede
call( )‟s definition with the keyword synchronized, as shown here:
class Callme
{
synchronized void call(String msg)
{
...
}
This prevents other threads from entering call( ) while another thread is using it. After
synchronized has been added to call( ), the output of the program is as follows:
[Hello]
[Synchronized]
[World]
While creating synchronized methods within classes that you create is an easy and effective
means of achieving synchronization, it will not work in all cases.
Imagine that you want to synchronize access to objects of a class that was not designed for
multithreaded access. That is, the class does not use synchronized methods. Further, this
class was not created by you, but by a third party, and you do not have access to the source
code. Thus, you can‟t add synchronized to the appropriate methods within the class. How can
access to an object of this class be synchronized? Fortunately, the solution to this problem is
quite easy: You simply put calls to the methods defined by this class inside a synchronized
block.
synchronized(object) {
// statements to be synchronized
}
Here, object is a reference to the object being synchronized. A synchronized block ensures
that a call to a method that is a member of object occurs only after the current thread has
successfully entered object‟s monitor.
class Callme
{
void call(String msg)
{
[Link]("[" + msg);
try
{
[Link](1000);
} catch (InterruptedException e)
{
[Link]("Interrupted");
}
[Link]("]");
}
}
class Caller implements Runnable
{
String msg;
Callme target;
Thread t;
public Caller(Callme targ, String s)
{
target = targ;
msg = s;
t = new Thread(this);
[Link]();
}
Interthread Communication
Threads also provide a secondary benefit: they do away with polling. Polling is usually
implemented by a loop that is used to check some condition repeatedly. Once the condition is
true, appropriate action is taken. This wastes CPU time.
For example, consider the classic queuing problem, where one thread is producing some
data and another is consuming it. To make the problem more interesting, suppose that the
producer has to wait until the consumer is finished before it generates more data. In a polling
system, the consumer would waste many CPU cycles while it waited for the producer to
produce. Once the producer was finished, it would start polling, wasting more CPU cycles
waiting for the consumer to finish, and so on. Clearly, this situation is undesirable.
• wait( ) tells the calling thread to give up the monitor and go to sleep until some other
thread enters the same monitor and calls notify( ).
• notify( ) wakes up a thread that called wait( ) on the same object.
• notifyAll( ) wakes up all the threads that called wait( ) on the same object. One of the
threads will be granted access.
if(flag==0)
{
try
{
[Link]("waiting....");
wait();
}catch(Exception e){}
}
[Link]-=amount;
[Link]("withdraw completed");
return amount;
}
public synchronized void deposit(int amount)
{
[Link]([Link]().getName()+" is going to deposit");
[Link]+=amount;
[Link]("deposit completed");
notifyAll();
flag=1;
}
}