Multithreading in Java
Multithreading in Java
1. Multithreading
2. Multitasking
3. Process-based multitasking
4. Thread-based multitasking
5. What is Thread
However, we use multithreading than multiprocessing because threads use a shared memory area.
They don't allocate separate memory area so saves memory, and context-switching between the
threads takes less time than process.
1) It doesn't block the user because threads are independent and you can perform multiple
operations at the same time.
3) Threads are independent, so it doesn't affect other threads if an exception occurs in a single
thread.
Multitasking
o Each process has an address in memory. In other words, each process allocates a separate
memory area.
o A process is heavyweight.
o Switching from one process to another requires some time for saving and loading registers,
memory maps, updating lists, etc.
o A thread is lightweight.
Threads are independent. If there occurs exception in one thread, it doesn't affect other threads. It
uses a shared memory area.
va provides Thread class to achieve thread programming. Thread class provides constructors and
methods to create and perform operations on a thread. Thread class extends Object class and
implements Runnable interface.
It is used to do an
2) Void run()
action for a thread.
It returns a reference
to the currently
4) static Thread currentThread()
executing thread
object.
It is used to suspend
13) Void suspend()
the thread.
It is used to resume
14) Void resume()
the suspended thread.
In Java, a thread always exists in any one of the following states. These states are:
1. New
2. Active
3. Blocked / Waiting
4. Timed Waiting
5. Terminated
New: Whenever a new thread is created, it is always in the new state. For a thread in the new state,
the code has not been run yet and thus has not begun its execution.
Active: When a thread invokes the start() method, it moves from the new state to the active state.
The active state contains two states within it: one is runnable, and the other is running.
o Runnable: A thread, that is ready to run is then moved to the runnable state. In the runnable
state, the thread may be running or may be ready to run at any given instant of time. It is the
duty of the thread scheduler to provide the thread time to run, i.e., moving the thread the
running state.
A program implementing multithreading acquires a fixed slice of time to each individual
thread. Each and every thread runs for a short span of time and when that allocated time
slice is over, the thread voluntarily gives up the CPU to the other thread, so that the other
threads can also run for their slice of time. Whenever such a scenario occurs, all those
threads that are willing to run, waiting for their turn to run, lie in the runnable state. In the
runnable state, there is a queue where the threads lie.
o Running: When the thread gets the CPU, it moves from the runnable to the running state.
Generally, the most common change in the state of a thread is from runnable to running and
again back to runnable.
Blocked or Waiting: Whenever a thread is inactive for a span of time (not permanently) then, either
the thread is in the blocked state or is in the waiting state.
For example, a thread (let's say its name is A) may want to print some data from the printer.
However, at the same time, the other thread (let's say its name is B) is using the printer to print
some data. Therefore, thread A has to wait for thread B to use the printer. Thus, thread A is in the
blocked state. A thread in the blocked state is unable to perform any execution and thus never
consume any cycle of the Central Processing Unit (CPU). Hence, we can say that thread A remains
idle until the thread scheduler reactivates thread A, which is in the waiting or blocked state.
When the main thread invokes the join() method then, it is said that the main thread is in the
waiting state. The main thread then waits for the child threads to complete their tasks. When the
child threads complete their job, a notification is sent to the main thread, which again moves the
thread from waiting to the active state.
If there are a lot of threads in the waiting or blocked state, then it is the duty of the thread scheduler
to determine which thread to choose and which one to reject, and the chosen thread is then given
the opportunity to run.
Timed Waiting: Sometimes, waiting for leads to starvation. For example, a thread (its name is A) has
entered the critical section of a code and is not willing to leave that critical section. In such a
scenario, another thread (its name is B) has to wait forever, which leads to starvation. To avoid such
scenario, a timed waiting state is given to thread B. Thus, thread lies in the waiting state for a
specific span of time, and not forever. A real example of timed waiting is when we invoke the sleep()
method on a specific thread. The sleep() method puts the thread in the timed wait state. After the
time runs out, the thread wakes up and start its execution from when it has left earlier.
Terminated: A thread reaches the termination state because of the following reasons:
o When a thread has finished its job, then it exists or terminates normally.
A terminated thread means the thread is no more in the system. In other words, the thread is dead,
and there is no way one can respawn (active after kill) the dead thread.
The following diagram shows the different states involved in the life cycle of a thread.
Implementation of Thread States
In Java, one can get the current state of a thread using the Thread.getState() method.
The java.lang.Thread.State class of Java provides the constants ENUM to represent the state of a
thread. These constants are:
It represents the runnable state.It means a thread is waiting in the queue to run.
It represents the blocked state. In this state, the thread is waiting to acquire a lock.
It represents the waiting state. A thread will go to this state when it invokes the Object.wait()
method, or Thread.join() method with no timeout. A thread in the waiting state is waiting for
another thread to complete its task.
It represents the timed waiting state. The main difference between waiting and timed waiting is the
time constraint. Waiting has no time constraint, whereas timed waiting has the time constraint. A
thread invoking the following method reaches the timed waiting state.
o sleep
Thread class:
Thread class provide constructors and methods to create and perform operations on a
thread.Thread class extends Object class and implements Runnable interface.
o Thread()
o Thread(String name)
o Thread(Runnable r)
2. public void start(): starts the execution of the thread.JVM calls the run() method on the
thread.
3. public void sleep(long miliseconds): Causes the currently executing thread to sleep
(temporarily cease execution) for the specified number of milliseconds.
5. public void join(long miliseconds): waits for a thread to die for the specified miliseconds.
10. public Thread currentThread(): returns the reference of currently executing thread.
14. public void yield(): causes the currently executing thread object to temporarily pause and
allow other threads to execute.
19. public void setDaemon(boolean b): marks the thread as daemon or user thread.
20. public void interrupt(): interrupts the thread.
21. public boolean isInterrupted(): tests if the thread has been interrupted.
22. public static boolean interrupted(): tests if the current thread has been interrupted.
Runnable interface:
The Runnable interface should be implemented by any class whose instances are intended
to be executed by a thread. Runnable interface have only one method named run().
Starting a thread:
The start() method of Thread class is used to start a newly created thread. It performs the
following tasks:
o When the thread gets a chance to execute, its target run() method will run.
FileName: Multi.java
3. System.out.println("thread is running...");
4. }
7. t1.start();
8. }
9. }
Output:
thread is running...
FileName: Multi3.java
3. System.out.println("thread is running...");
4. }
5.
9. t1.start();
10. }
11. }
Output:
thread is running...
If you are not extending the Thread class, your class object would not be treated as a thread
object. So you need to explicitly create the Thread class object. We are passing the object of
your class that implements Runnable so that your class run() method may execute.
We can directly use the Thread class to spawn new threads using the constructors defined
above.
FileName: MyThread1.java
2. {
3. // Main method
5. {
6. // creating an object of the Thread class using the constructor Thread(String name)
8.
10. t.start();
13. System.out.println(str);
14. }
15. }
Output:
My first thread
FileName: MyThread2.java
2. {
4. {
6. }
7.
8. // main method
10. {
13.
14. // creating an object of the class Thread using Thread(Runnable r, String name)
16.
17. // the start() method moves the thread to the active state
18. th1.start();
19.
22. System.out.println(str);
23. }
24. }
Output:
My new thread
Now the thread is running ...
A component of Java that decides which thread to run or execute and which thread to wait
is called a thread scheduler in Java. In Java, a thread is only chosen by a thread scheduler if
it is in the runnable state. However, if there is more than one thread in the runnable state, it
is up to the thread scheduler to pick one of the threads and ignore the other ones. There are
some criteria that decide which thread will execute first. There are two factors for
scheduling a thread i.e. Priority and Time of arrival.
Priority: Priority of each thread lies between 1 to 10. If a thread has a higher priority, it
means that thread has got a better chance of getting picked up by the thread scheduler.
Time of Arrival: Suppose two threads of the same priority enter the runnable state, then
priority cannot be the factor to pick a thread from these two threads. In such a case, arrival
time of thread is considered by the thread scheduler. A thread that arrived first gets the
preference over the other threads.
On the basis of the above-mentioned factors, the scheduling algorithm is followed by a Java
thread scheduler.
In this scheduling algorithm, the scheduler picks the threads thar arrive first in the runnable
queue. Observe the following table:
t1 0
t2 1
t3 2
t4 3
In the above table, we can see that Thread t1 has arrived first, then Thread t2, then t3, and
at last t4, and the order in which the threads will be processed is according to the time of
arrival of threads.
Hence, Thread t1 will be processed first, and Thread t4 will be processed last.
Time-slicing scheduling:
Usually, the First Come First Serve algorithm is non-preemptive, which is bad as it may lead
to infinite blocking (also known as starvation). To avoid that, some time-slices are provided
to the threads so that after some time, the running thread has to give up the CPU. Thus, the
other waiting threads also get time to run their job.
In the above diagram, each thread is given a time slice of 2 seconds. Thus, after 2 seconds,
the first thread leaves the CPU, and the CPU is then captured by Thread2. The same process
repeats for the other threads too.
Preemptive-Priority Scheduling:
The name of the scheduling algorithm denotes that the algorithm is related to the priority of
the threads.
Suppose there are multiple threads available in the runnable state. The thread scheduler
picks that thread that has the highest priority. Since the algorithm is also preemptive,
therefore, time slices are also provided to the threads to avoid starvation. Thus, after some
time, even if the highest priority thread has not completed its job, it has to release the CPU
because of preemption.
Let's understand the working of the Java thread scheduler. Suppose, there are five threads
that have different arrival times and different priorities. Now, it is the responsibility of the
thread scheduler to decide which thread will get the CPU first.
The thread scheduler selects the thread that has the highest priority, and the thread begins
the execution of the job. If a thread is already in runnable state and another thread (that has
higher priority) reaches in the runnable state, then the current thread is pre-empted from
the processor, and the arrived thread with higher priority gets the CPU time.
When two threads (Thread 2 and Thread 3) having the same priorities and arrival time, the
scheduling will be decided on the basis of FCFS algorithm. Thus, the thread that arrives first
gets the opportunity to execute first.
We know that a thread with high priority will get preference over lower
priority threads when it comes to the execution of threads. However, there
can be other scenarios where two threads can have the same priority. All of
the processing, in order to look after the threads, is done by the Java thread
scheduler. Refer to the following example to comprehend what will happen if
two threads have the same priority.
FileName: ThreadPriorityExample1.java
33.
34. // th1 thread is the child of the main thread
35. // therefore, the th1 thread also gets the priority 7
36.
37. // Displaying the priority of the current thread
38. System.out.println("Priority of the thread th1 is : " + th1.getPri
ority());
39. }
40. }
Output:
Advertisement
Explanation: If there are two threads that have the same priority, then one
can not predict which thread will get the chance to execute first. The
execution then is dependent on the thread scheduler's algorithm (First Come
First Serve, Round-Robin, etc.)
Synchronization in Java
Synchronization in Java is the capability to control the access of multiple
threads to any shared resource.
Java Synchronization is better option where we want to allow only one thread
to access the shared resource.
Types of Synchronization
There are two types of synchronization
1. Process Synchronization
2. Thread Synchronization
Thread Synchronization
There are two types of thread synchronization mutual exclusive and inter-
thread communication.
1. Mutual Exclusive
1. Synchronized method.
2. Synchronized block.
3. Static synchronization.
2. Cooperation (Inter-thread communication in java)
Mutual Exclusive
Mutual Exclusive helps keep threads from interfering with one another while
sharing data. It can be achieved by using the following three ways:
TestSynchronization1.java
1. class Table{
2. void printTable(int n){//method not synchronized
3. for(int i=1;i<=5;i++){
4. System.out.println(n*i);
5. try{
6. Thread.sleep(400);
7. }catch(Exception e){System.out.println(e);}
8. }
9.
10. }
11. }
12.
13. class MyThread1 extends Thread{
14.Table t;
15. MyThread1(Table t){
16.this.t=t;
17. }
18.public void run(){
19. t.printTable(5);
20.}
21.
22.}
23. class MyThread2 extends Thread{
24.Table t;
25. MyThread2(Table t){
26.this.t=t;
27. }
28.public void run(){
29. t.printTable(100);
30.}
31. }
32.
33. class TestSynchronization1{
34.public static void main(String args[]){
35. Table obj = new Table();//only one object
36.MyThread1 t1=new MyThread1(obj);
37. MyThread2 t2=new MyThread2(obj);
38.t1.start();
39. t2.start();
40.}
41. }
Output:
5
100
10
200
15
300
20
400
25
500
TestSynchronization2.java
Advertisement
5
10
15
20
25
100
200
300
400
500
TestSynchronization3.java
Advertisement
5
10
15
20
25
100
200
300
400
500
o wait()
o notify()
o notifyAll()
1) wait() method
The wait() method causes current thread to release the lock and wait until either another thread
invokes the notify() method or the notifyAll() method for this object, or a specified amount of
time has elapsed.
The current thread must own this object's monitor, so it must be called from the synchronized
method only otherwise it will throw exception.
Method Description
2) notify() method
The notify() method wakes up a single thread that is waiting on this object's
monitor. If any threads are waiting on this object, one of them is chosen to be
awakened. The choice is arbitrary and occurs at the discretion of the
implementation.
Syntax:
Syntax:
wait() sleep()
The wait() method releases the lock. The sleep() method doesn't release th
It should be notified by notify() or notifyAll() methods After the specified amount of time, sl
completed.
Test.java
1. class Customer{
2. int amount=10000;
3.
4. synchronized void withdraw(int amount){
5. System.out.println("going to withdraw...");
6.
7. if(this.amount<amount){
8. System.out.println("Less balance; waiting for deposit...");
9. try{wait();}catch(Exception e){}
10.}
11. this.amount-=amount;
12.System.out.println("withdraw completed...");
13. }
14.
15. synchronized void deposit(int amount){
16.System.out.println("going to deposit...");
17. this.amount+=amount;
18.System.out.println("deposit completed... ");
19. notify();
20.}
21. }
22.
23. class Test{
24.public static void main(String args[]){
25. final Customer c=new Customer();
26.new Thread(){
27. public void run(){c.withdraw(15000);}
28.}.start();
29. new Thread(){
30.public void run(){c.deposit(10000);}
31. }.start();
32.
33. }}
Output:
going to withdraw...
Less balance; waiting for deposit...
going to deposit...
deposit completed...
withdraw completed