Thread: Multithreading, Runnable, Thread Class, Callable, DeadLock, Synchronization
Thread is a part of program execution which executes with different set of variable values .
In JVM multiple threads can be running concurrently to server a purpose.
When multiple thread executes in program that is called Multi-Threading.
Life Cycle Of Thread: Thread could be in 5 states : 1) New 2) Runnable 3) Running 4) Waiting/Blocked 5) Dead/Terminated
Runnable: The thread is in runnable state after invocation of start() method, but the
thread scheduler has not selected it to be the running thread.
Thread is a part of program execution which executes with different set of variable values .
In JVM multiple threads can be running concurrently to server a purpose.
When multiple thread executes in program that is called Multi-Threading.
Life Cycle Of Thread: Thread could be in 5 states : 1) New 2) Runnable 3) Running 4) Waiting/Blocked 5) Dead/Terminated
New: The thread is in new state if you create an instance of Thread class but before the invocation of start() method.
Running: The thread is in running state if the thread scheduler has selected it.
Waiting/Blocked:This is the state when the thread is still alive, but is currently not eligible to run.
Terminated: A thread is in terminated or dead state when its run() method exits.
Thread starts with New and goes to Runnable state after that it could be either in waiting state or Running . If the thread gets the resources then it goes to running state or it keeps on waiting.
A thread after running state can be either in dead state on completion of task or it could be in waiting state while waiting for other resources to complete the task.
Every thread has a priority value which may be set as default or some custom priority.
Thread with high value of priority are executed in preference to low priority.
Java thread priorities are in the range between MIN_PRIORITY (which is 1) and MAX_PRIORITY (which is10). By default, every thread is given priority NORM_PRIORITY (a constant of 5).
Thread in java is accomplished using java.lang.Thread
Following methods of thread are most commonly used :
There are two ways to create a new thread :
1) Make a subclass of Thread class and override run() method.
Example :
Then we can run the above method as following :
And the above thread can be run as
If we are not adding anything to the current behavior of thread we should be creating Threads by implementing Runnable because :
By extending Thread class, we can extend another class as java does not allow more than one class to extend
However by implementing Runnable,we can still extend one class and the same purpose of thread get served as well.
Another significant benefit of implementing the Runnable is : using Runnable , Object gets shared between multiple threads. However, when we extend Thread class , each thread creates a unique object which uses a lot of memory.
A small example will demonstrate that :
The output of the above program would be :
Thread starts with New and goes to Runnable state after that it could be either in waiting state or Running . If the thread gets the resources then it goes to running state or it keeps on waiting.
A thread after running state can be either in dead state on completion of task or it could be in waiting state while waiting for other resources to complete the task.
Every thread has a priority value which may be set as default or some custom priority.
Thread with high value of priority are executed in preference to low priority.
Java thread priorities are in the range between MIN_PRIORITY (which is 1) and MAX_PRIORITY (which is10). By default, every thread is given priority NORM_PRIORITY (a constant of 5).
Thread in java is accomplished using java.lang.Thread
Following methods of thread are most commonly used :
getName(): to get the name of threadgetPriority(): to get the priorityisAlive(): Determine if a thread is still runningjoin(): Wait for a thread to terminaterun(): Entry point for the thread- sleep(long miliseconds): it causes thread to sleep for specified time.
start(): start a thread by calling its run() method- stop(): is used to stop the thread(depricated).
- join(long milliseconds): waits for a thread to die for the specified milliseconds.
There are two ways to create a new thread :
1) Make a subclass of Thread class and override run() method.
Example :
class ExampleThread extends Thread {
String threadName
ExampleThread(long threadName) {
this.threadName = threadName;
}
public void run() {
System.out.println("Current thread name is: " + threadName);
}
}
Then we can run the above method as following :
ExampleThread et = new ExampleThread("Example");
et.start();
2) Another way is to implement Runnable and override run() method
Also we need to create an object of the class and pass as an argument to Thread class
and call the start method which in turn calls run() method
Example :
class ExampleThread implements Runnable {
String threadName
ExampleThread(long threadName) {
this.threadName = threadName;
}
public void run() {
System.out.println("Current thread name is: " +threadName);
}
}
And the above thread can be run as
ExampleThread et = new ExampleThread("Example");
new Thread(et).start();
If we are not adding anything to the current behavior of thread we should be creating Threads by implementing Runnable because :
By extending Thread class, we can extend another class as java does not allow more than one class to extend
However by implementing Runnable,we can still extend one class and the same purpose of thread get served as well.
Another significant benefit of implementing the Runnable is : using Runnable , Object gets shared between multiple threads. However, when we extend Thread class , each thread creates a unique object which uses a lot of memory.
A small example will demonstrate that :
class RunnableExample implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("RunnableExample Thread Counter value " + counter);
}
}
class ExampleExtendThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExampleExtendThread Thread Counter Value: " + counter );
}
}
public class ThreadNRunnableExample {
public static void main(String args[]) throws Exception {
//Multiple threads share the same object.
RunnableExample examp = new RunnableExample();
Thread t1 = new Thread(examp);
t1.start();
Thread.sleep(100); //adding a little wait time for other thread to start
Thread t2 = new Thread(examp);
t2.start();
Thread.sleep(100);
Thread t3 = new Thread(examp);
t3.start();
ExampleExtendThread tc1 = new ExampleExtendThread();
tc1.start();
Thread.sleep(100);
ExampleExtendThread tc2 = new ExampleExtendThread();
tc2.start();
Thread.sleep(100);
ExampleExtendThread tc3 = new ExampleExtendThread();
tc3.start();
}
}
The output of the above program would be :
RunnableExample Thread Counter value 1
RunnableExample Thread Counter value 2
RunnableExample Thread Counter value 3
ExampleExtendThread Thread Counter Value: 1
ExampleExtendThread Thread Counter Value: 1
ExampleExtendThread Thread Counter Value: 1
In case of runnable, because same object is being used so counter value is getting incremented. While in case of extending thread different thread is used so counter value is 1 for all three threads
**You should call start() method (which in-turn call run()) always to start a new thread , otherwise calling run() method directly will execute in the current thread.
For example say we created two thread object T1 and T2.
And if we call the run() method directly on thread T1 and T2 then both will be executed synchronous which will not server purpose of multi thread.
On calling start method , it first creates a new thread by executing some native codes and then call the turn method.
Callable: It is also an interface which helps to run multiple threads in parallel , similar to Runnable but with difference that callable can return value to the caller and also can throw checked exception .
Before Executor Framework , thread management was done by user.
ExecutorService(a more refined sub interface of Executor) is used to launch task, manage life cycle of thread. It also provide support for Callable and Future.
The java.util.concurrent package provides three executor interfaces:
1) Create ExecutorService object
2) Pass runnable/callable object to Executor subject and submit the task.
3) Get status or wait for completion of task.
There is separate page written for Executor Framework. Please read that for more details.
There is a method call() in the Callable interface which must be overridden for processing/computation.
To run the Callable , submit() method of ExecutorService is used.
When you pass Callable to thread pool , it execute the Callable using one thread and returns Future object which holds the result of computation .
There is a method get() which can be called on Future object which will return the result of computation if it is complete or it will block if it is not complete.
If blocking is not required then we can use another overloaded get method with timeout values.
We can check if computation is done or not using isDone() method , it can be cancelled using Future.cancel()
Example demonstrating how to use Callable :
RunnableExample Thread Counter value 2
RunnableExample Thread Counter value 3
ExampleExtendThread Thread Counter Value: 1
ExampleExtendThread Thread Counter Value: 1
ExampleExtendThread Thread Counter Value: 1
In case of runnable, because same object is being used so counter value is getting incremented. While in case of extending thread different thread is used so counter value is 1 for all three threads
**You should call start() method (which in-turn call run()) always to start a new thread , otherwise calling run() method directly will execute in the current thread.
For example say we created two thread object T1 and T2.
And if we call the run() method directly on thread T1 and T2 then both will be executed synchronous which will not server purpose of multi thread.
On calling start method , it first creates a new thread by executing some native codes and then call the turn method.
Callable: It is also an interface which helps to run multiple threads in parallel , similar to Runnable but with difference that callable can return value to the caller and also can throw checked exception .
Before Executor Framework , thread management was done by user.
ExecutorService(a more refined sub interface of Executor) is used to launch task, manage life cycle of thread. It also provide support for Callable and Future.
The java.util.concurrent package provides three executor interfaces:
- Executor – a simple interface that supports launching new taks
- ExecutorService – a sub interface of Executor, which adds features that help manage the life cycle , both of the individual tasks and the executor itself. It is very similar to thread pool.
- ScheduledExecutorService – a sub interface of ExecutorService, supports future and/or periodic execution of tasks.
1) Create ExecutorService object
2) Pass runnable/callable object to Executor subject and submit the task.
3) Get status or wait for completion of task.
There is separate page written for Executor Framework. Please read that for more details.
There is a method call() in the Callable interface which must be overridden for processing/computation.
To run the Callable , submit() method of ExecutorService is used.
When you pass Callable to thread pool , it execute the Callable using one thread and returns Future object which holds the result of computation .
There is a method get() which can be called on Future object which will return the result of computation if it is complete or it will block if it is not complete.
If blocking is not required then we can use another overloaded get method with timeout values.
We can check if computation is done or not using isDone() method , it can be cancelled using Future.cancel()
Example demonstrating how to use Callable :
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ExampleCallable {
public static void main(String args []) throws InterruptedException, ExecutionException {
// creating thread pool to execute task which implements Callable
ExecutorService es = Executors.newSingleThreadExecutor();
Future results1 = es.submit(new FactorialCalculator(5));
Future results2 = es.submit(new FactorialCalculator(10));
System.out.println("Calling get methods of Future to get results of submitted tasks");
long factorial1 = (long) results1.get();
System.out.println("factorial of 5 is : " + factorial1);
long factorial2 = (long) results2.get();
System.out.println("factorial of 10 is : " + factorial2);
}
}
class FactorialCalculator implements Callable {
private int number;
public FactorialCalculator(int number){
this.number = number;
}
@Override
public Long call() throws Exception {
return factorial(number);
}
private long factorial(int n) throws InterruptedException {
long result = 1;
while (n != 0) {
result = n * result;
n = n - 1;
Thread.sleep(100);
}
return result;
}
}
tTwo popular methods of ExecutorService :
shutdown()will just tell the executor service that it can't accept new tasks.- But the already submitted tasks continue to run
shutdownNow()will do the same AND will try to cancel the already submitted tasks by interrupting he relevant threads.- Note that if your tasks ignore the interruption,
shutdownNowwill behave exactly the same way asshutdown.
MultiThreading is when you are using multiple threads of an object to do a task or do different tasks. Issues occurs when we dont manage those threads properly. Following things should be keep in mind while using multi threading : 1) Try to use local variables as the shared variable if not properly managed would give wrong result. Specially avoid using static variables. 2) Minimize locking scope as the code inside the locked block can not be executed concurrently. If possible , try using volatile variables as an alternative to synchonization 3) Try using Immutable classes like String wherever possible. 4) Prefer Synchronized block over synchronized method as that would help to get better performance. DeadLock: Deadlock occurs when two or more thread are blocked forever.
public class DeadLockExample {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
DLDemo1 T1 = new DLDemo1();
DLDemo2 T2 = new DLDemo2();
T1.start();
T2.start();
}
private static class DLDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class DLDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
Synchronization: Synchronization is required when two or more threads need a shared resource to access. If properly the resources are not synchronized then the results after using shared resources may not be correct.
So there is a need to synchronize the action of multiple threads and make sure that only one thread can access the resource at a given point in time. This is implemented using a concept called monitors. Each object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor.
So either we could synchronize in a way that only one thread access shared resources at a time and other keeps on waiting and access immediately when the thread which is holding the lock, exits the lock.
There could be other way that the thread which is holding the lock notifies the other threads that the resources is available.
We could synchronize a method or a block depending upon the usage.
We should always try to synchronize block as that gives a better performance because the resources outside the block in the method can still be used by other threads
Scope of synchronized block is generally smaller than method. It is used to lock on object for the shared resource.
Synchronized keyword prevents reordering of statements by compiler which which can cause subtle concurrency issues otherwise.
When the static method is synchronized then there is a class level lock otherwise it would be an instance level lock
Java synchronized keyword is re-entrant in nature it means if a java synchronized method calls another synchronized method which requires same lock then current thread which is holding lock can enter into that method without acquiring lock.
Java synchronization can not be used on null , it will throw NullPointerException if the object is null.
Its possible that both static synchronized and non static synchronized method can run simultaneously or concurrently because they lock on different object.
synchronized can not be used with constructor
Dont synchronize on String because literal and interned string are global object as those gets stored in String Pool which may get used by other part of code .
Comments
Post a Comment