Chapter 04
Chapter 04
3053)
Chapter 04
Multi-Threading
1
Objectives
After studying this chapter, students should be able to:
Learn about Thread and Process
provides an overview of threads
provides an Life Cycle of a Thread
3
Process
A process is an independent unit of execution that
has its own memory space and resources.
Each process runs in a separate memory space, and
they are isolated from each other.
Processes are used to achieve parallel execution and
can perform multiple tasks simultaneously.
Processes are more isolated from each other, which
provides better security and fault tolerance.
Interprocess communication requires more
complex mechanisms, such as interprocess
communication (IPC) techniques like pipes, sockets,
or shared memory.
Context switching between processes is slower
compared to threads due to the need to switch
memory spaces.
4
Multitasking
Multitasking is a process of executing multiple tasks simultaneously, even on a
single processor system, by rapidly switching between them.
It involves the creation and management of threads and process, which are
execution that can run simultaneously and independently.
We use multitasking to utilize the CPU.
Multitasking can be achieved in two ways
1. Process-based Multitasking (Multiprocessing)
2. Thread-based Multitasking (Multithreading)
Process-based Multitasking (Multiprocessing)
is a form of multitasking where multiple processes execute concurrently on a
computer system.
Each process has its own memory space and resources, and they run independently of
each other.
Process-based multitasking is typically implemented by the operating system, which
5
Cont.
Cost of communication between the process is high.
Switching from one process to another requires some time for saving and
6
Multithreading
Multithreading is a Java feature that allows concurrent execution of two or more
parts of a program for maximum utilization of CPU.
Java Multithreading is mostly used in games, animation, etc.
Multithreading program contains two or more threads that can run concurrently
Threads are independent because they all have separate path of execution that’s the
reason if an exception occurs in one thread, it doesn’t affect the execution of other
threads.
Every Java program has at least one thread - the main thread.
All threads of a process share the common memory.
JVM manages threads and schedules them for execution.
Why Multithreading in java
Parallel Programming
● The main reasons is to make a task run parallel to another task e.g. drawing and event handling. GUI
applications e.g. Swing and Java FX GUIs are the best examples of multi-threading in Java.
● In a typical GUI application, the user initiates an action
● e.g. downloading a file from the network or loading games modules from hard disk.
7
Cont.
To take full advantage of CPU power
● The main reasons is to improve the throughput of the application by utilizing full CPU power.
● For example, if you have got 32 core CPUs and you are only using 1 of them for serving 1000
clients and assuming that your application is CPU bound, you can improve throughput to 32 times
by using 32 threads, which will utilize all 32 cores of your CPU.
For reducing response time
● We can also use multiple threads to reduce response time by doing fast computation by dividing a
big problem into smaller chunks and processing them by using multiple threads.
To sever multiple clients at the same time
● Using multiple threads significantly improves an application's performance in a client-server
application.
● A multi-threaded server means multiple clients can connect to the server at the same time.
● This means, next client doesn't have to wait until your application finish processing request of the
previous client. In below example, multiple requests are processing by our multi-threaded server at
the same time.
8
Life cycle of Thread
A thread moves several states in its life cycle. This life cycle is controlled by JVM. These possible
states are:
New
In this phase, the thread is created using class “Thread class”.
The thread is in the new state if you create an instance of Thread class but before the invocation of
the start() method.
It is also known as born thread.
Transitions to Runnable state after it is started by start() method.
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.
Runnable (ready)
In this stage, the instance of the thread is invoked with a start method.
Running
When the thread gets the CPU, it moves from the runnable to the running state.
Waiting (Non-Runnable/Blocked)
A thread enters this state when it is temporarily in an inactive state. i.e., it is still alive but is not
eligible to run.
It can be in waiting, sleeping, or blocked state.
9
Cont.
Dead (terminated)
A thread is terminated due to the following reasons:
Either its run() method exists normally, i.e., the thread’s code has executed the program.
Or due to some unusual errors like segmentation fault or an unhandled exception.
Life cycle of Threads
10
Cont.
● Thread class available in java.lang package.
● Thread class is used to create and manage the thread.
Some of the constructors:
Thread()
Thread(String s)
Thread(Runnable r)
Thread(Runnable r, String s)
● Here r is a reference to an object that implements the Runnable interface and s is a string used to
provide thread name.
Some methods of Thread class:
● static Thread currentThread() – returns the reference to the currently executing thread.
● static void sleep(long msec) throws InterruptedException – it makes thread
to wait in msec.
● static void sleep(long msec, int nsec) throws InterruptedException – it
makes thread to wait in msec plus nsec.
● static void yield() – provide the control of the processor to the other thread.
11
Cont.
● String getName() – returns the name of the thread
● int getPriority() – returns the priorities of thread
● void setName(String s) – set the name for the thread
● void setPriority(int p) – set the priority for the thread
● boolean isAlive() – returns true if thread is alive. Otherwise false
● void start() – starts the thread
● void stop() – used to terminate the thread
● void run() – new thread begins its life inside this method
● void join(long msec, int nsec) throws InterruptedException – caller to wait
a maximum msec until this thread dies.
Some constants:
● Thread class defines three int constants that are used to specify the priority of a thread.
MAX_PRIORITY (default is 10)
MIN_PRIORITY (default is 1)
NORM_PRIORITY (default is 5)
12
Creating Threads
To create threads in Java, you have two main approaches:
1. Extending the Thread class
2. Implementing the Runnable interface.
Extending the Thread class:
Create a new class that extends the Thread class.
Override the run() method in your class. This method contains the code that will
be executed when the thread is started.
Optionally, you can override other methods such as start() for custom thread
startup behavior.
Instantiate an object of your custom thread class.
Call the start() method on the thread object to start the execution of the thread.
This will internally call the run() method.
13
Cont.
Example
class MyThread extends Thread { Output
@Override Thread is running.
public void run() {
// Code to be executed by the
thread
System.out.println("Thread is
running.");
}
}
will throw an InterruptedException so that the thread will know that it was
awakened by an interrupt and won't have to check to see if the timer expired.
17
Cont.
Example for sleep()
public class SleepExp1 extends public static void main(String
Thread { args[]){
public void run(){ SleepExp1 thread1=new
for(int i=1;i<5;i++){ SleepExp1();
try { SleepExp1 thread2=new
Thread.sleep(2000); SleepExp1();
thread1.start();
}catch(InterruptedExcep OUTPUT
thread2.start();
tion e){ 1
}
1
} 2
System.out.println(e);} 2
3
System.out.println(i); 3
} 4
} 4
18
Cont.
public static void yield()
● The Thread.yield() method is like Thread.sleep(), but instead of sleeping, it simply
pauses the currently executing thread and will give a chance to other waiting threads
of the same priority.
● If in case there are no waiting threads or if all the waiting threads have low
priority then the same thread can continue its execution.
CHANGEABLE
public class JavaYieldExp extends OUTPUT
Thread{ Thread-0 in control
public void run(){ Thread-1 in control
for (int i=0; i<3 ; i++){ Thread-1 in control
main in control
System.out.println(Thread.currentThread( Thread-1 in control
). Thread-0 in control
getName() + " in control"); main in control
} Thread-0 in control
} main in control
19
Cont.
public static void
main(String[]args){ b) getName() & setName() method
JavaYieldExp thread1 =
new JavaYieldExp();
public class FindMainThread {
JavaYieldExp thread2 = public static void main(String
new JavaYieldExp(); args[]){
thread1.start(); Thread th=new Thread();
thread2.start();
System.out.println("thread
for (int i=0; i<3; i++){
name: " + th.getName());
thread1.yield();
th.setName("Main");
System.out.println(“main”+Thread. System.out.println("thread
currentThread(). name: " + th.getName());
getName() + " in
}
control");
} } OUTPUT
} thread name: Thread-0
} thread name: Main
20
Thread priorities
● Each java thread has its own priority which decides the order of thread to be schedule.
● The threads of equal priority will be given same treatment by java scheduler. And they
will follow the FCFS (First Come First Serve) algorithm.
● User can also set the priority of thread by using the setPriority() method as follow:
ThreadName.setPriority(int Number);
● Here the number is integer value between 1 to 10, Here 1 is minimum priority, 10 is
maximum priority.
● The Thread class defines few priority constants:
o
MIN_PRIORITY = 1
o
NORM_PRIORITY = 5
o
MAX_PRIORITY = 10
● In any Thread the default priority is NORM_PRIORITY.
21
Cont.
public final void setPriority()
Output:
class JavaSetPriorityExp1 extends Priority of thread is: 10
Thread{
public void run(){
System.out.println("Priority
of thread is: "+
Thread.currentThread().getPriority()
);
}
public static void main(String
args[]){
JavaSetPriorityExp1 t1=new
JavaSetPriorityExp1();
t1.setPriority(Thread.MAX_PRIORITY);
t1.start(); }
} 22
Daemon Threads
● It is a low-priority thread that runs in the background to perform tasks such as garbage
collection.
● Also, it is a service provider thread that provides services to the user thread.
● It’s mentioned that a Java program exits when all of its threads have completed, but
this is not exactly correct.
What about the hidden system threads, such as the garbage collection thread and
others created by the JVM? We have no way of stopping these.
If those threads are running, how does any Java program ever exit?
● These system threads are called daemon threads. A Java program actually exits when
all its non-daemon threads have completed.
● Any thread can become a daemon thread.
You can indicate a thread is a daemon thread by calling the Thread.setDaemon()
method.
You might want to use daemon threads for background threads that you create in
your programs, such as timer threads or other deferred event threads, which are
only useful while there are other non-daemon threads running.
23
Cont.
public final boolean isDaemon() public static void main(String[]
args)
public class JavaIsDaemonExp extends {
Thread{ JavaIsDaemonExp thread1=new
public void run(){ JavaIsDaemonExp();
//checking for daemon thread JavaIsDaemonExp thread2=new
JavaIsDaemonExp();
if(Thread.currentThread().isDaemon()){ JavaIsDaemonExp thread3=new
System.out.println("daemon JavaIsDaemonExp();
thread work"); thread1.setDaemon(true);
} thread1.start();
else { thread2.start();
System.out.println("user thread3.start();
thread work"); }
} Output:
} daemon thread work
}
user thread work
user thread work
24
Sharing Variables
● For multiple threads to be useful in a program, they have to have some way to
communicate or share their results with each other.
● The simplest way for threads to share their results is to use shared variables.
Race condition & How to Solve it:
● Race conditions occur when multiple, asynchronously executing threads access the
same object (called a shared resource) returning unexpected (wrong) results.
Example:
Threads often need to share a common resource i.e. a file, with one thread reading
from the file while another thread writes to the file. They can be avoided by
synchronizing the threads which access the shared resource.
25
Cont.
Example for Unsynchronized Threads class MyThread2 extends Thread{
class Table { Table t;
void printTable(int n){ MyThread2(Table t){
//method not synchronized this.t=t;
for(int i=1;i<=5;i++){ }
System.out.println(n*i); public void run(){
try{ t.printTable(100);
Thread.sleep(400); }
}catch(Exception e){ }
System.out.println(e);} class Use{
} Output public static void main(String args[]
}} 5 {
class MyThread1 extends Thread{ 100 Table obj = new Table();//only one
Table t; 10 object
MyThread1(Table t){ 200 MyThread1 t1=new MyThread1(obj);
this.t=t; 300 MyThread2 t2=new MyThread2(obj);
} 15
t1.start();
public void run(){ 400
20
t2.start();
t.printTable(5); }
25
} 500 }
} 26
Thread synchronization
Synchronization is needed in a multi-threading application to ensure that one thread is
updating a shared data other threads wait and get the updated value.
Synchronization uses a lock to represent the shared data to allow each thread to use the
lock status to Synchronize with each other.
Synchronization code block is a unit of code in a thread that requires synchronization
on a particular lock.
More synchronization locks or longer synchronization code blocks slow down
application performance.
Synchronization is a programming technique that involves 3 elements:
Lock: An object with two states: locked and unlocked.
Synchronized Block: A block of statements that is associated with a lock.
Synchronization Rule: When a synchronized block is encountered in a thread of
execution, the associated lock will be checked.
27
Cont.
● If the lock is locked, the execution will be stopped until the lock is unlocked.
● If the lock is unlocked, the synchronized block of statements will be executed.
● When the execution reaches the end of the synchronized block, the lock will be
unlocked.
● With this rule, two synchronized blocks associated with same lock will never be
executed at the same time.
How Java supports Synchronization
● Instead of let the programmers to design their own locks, manage the synchronization
blocks, and apply the synchronization rules, Java offers a synchronization monitor on
each instance of the Object class, so it can be used as a synchronization lock.
● Since all classes are sub classes of Object, all objects in Java can be used as
synchronization locks.
Java also offers three ways to define synchronized blocks.
28
Cont.
Method 1: Static Synchronized Method: Method 2: Synchronized Method:
class class_name { class class_name {
static synchronized return_type synchronized return_type
method_name() { method_name() {
//statement block //statement block
} }
} }
● All the statements in the method become the ● All the statements in the method
synchronized block, and the class object is become the synchronized block, and
the lock. the instance object is the lock.
Method 3: Synchronized Block:
class class_name {
return_type method_name() { All the statements specified in the
synchronized (object) { parentheses of the synchronized statement
statement block become the synchronized block, and the
}
} object specified in the statement is the
} lock. 29
Cont.
Example for Synchronized Threads
class Table{ class MyThread1 extends
synchronized void printTable(int n){ Thread{
//method synchronized Table t;
for(int i=1;i<=5;i++){ MyThread1(Table t){
System.out.println(n*i); this.t=t;
try{ }
Thread.sleep(400); public void run(){
}catch(Exception e){ t.printTable(5);
System.out.println(e);} }
} }
}} class MyThread2 extends Thread{
Table t;
MyThread2(Table t){
this.t=t;
}
public void run(){
t.printTable(100);
}
}
30
Cont.
class Use{
public static void main(String args[]){
Table obj = new Table(); //only one Output
object 5
MyThread1 t1=new MyThread1(obj); 10
MyThread2 t2=new MyThread2(obj); 15
t1.start(); 20
t2.start(); 25
} 100
} 200
300
400
500
31
Cont.
Important point of synchronized keyword in Java
1. Synchronized keyword in Java is used to provide mutual exclusive access of a shared
resource with multiple threads in Java.
2. Synchronization in java guarantees that no two threads can execute a synchronized
method which requires same lock simultaneously or concurrently.
3. When ever a thread enters into java synchronized method or block it acquires a lock
and whenever it leaves java synchronized method or block it releases the lock.
4. One Major disadvantage of java synchronized keyword is that it doesn't allow
concurrent read which you can implement using
java.util.concurrent.locks.ReentrantLock.
5. Java synchronized keyword incurs performance cost.
6. You cannot apply java synchronized keyword with variables.
32
Thread Communication
In multi-threaded program to exchange data and synchronize their activities.
Threads are lightweight execution units that share the same memory space within a
or inconsistent state issues. Techniques like locks, mutexes, and semaphores are often
used to coordinate access to shared memory.
Message Passing:
In this approach, threads communicate by sending messages to each other.
A thread can send a message containing data or a signal to another thread, which
receiver, while in asynchronous message passing, the sender continues its execution
without waiting for a reply 33
Cont.
Condition Variables:
Condition variables are synchronization primitives used to signal and control the execution flow
between threads.
They allow threads to wait for a certain condition to become true before proceeding.
Threads can wait on a condition variable until another thread signals that the condition has been
met.
Condition variables are often used in conjunction with mutexes to protect shared data and coordinate
threads' activities.
Events and Signals:
Events and signals are mechanisms provided by the operating system or threading libraries to notify
threads about specific occurrences or state changes.
Threads can wait for an event or signal to be raised and resume execution when it occurs.
Events and signals are useful for scenarios where a thread needs to wait for something to happen
before proceeding.
Thread Synchronization Primitives:
Thread synchronization primitives such as locks, mutexes, semaphores, and barriers are
commonly used to coordinate and control the execution of threads.
These mechanisms ensure that only one thread can access a shared resource at a time or that a
certain number of threads have reached a specific point before proceeding. They help prevent data
races and enforce order and synchronization among threads. 34
Thread Pooling and Executors
Thread pooling and executors are concepts and frameworks used in concurrent
programming to manage the execution of tasks by reusing a pool of worker threads.
They provide a higher-level abstraction for managing concurrency and simplify the
35
Cont.
Executors:
The Executors class is a utility class in Java that provides factory methods for creating instances of
ExecutorService.
It offers several predefined implementations of thread pools, such as newFixedThreadPool,
newCachedThreadPool, and newSingleThreadExecutor, each with different characteristics and
behaviors.
Tasks can be submitted to an executor for execution using various methods provided by the
ExecutorService interface, such as execute(Runnable), submit(Callable), or
invokeAny(Collection).
These methods allow tasks to be executed asynchronously, returning Future objects that can be used
to obtain the results or manage the execution status of the tasks.
Executors provide methods to gracefully shut down and terminate the executor and its associated
thread pool.
The shutdown() method initiates a graceful shutdown, allowing already submitted tasks to
complete but rejecting new tasks. The shutdownNow() method attempts to cancel and interrupt all
running tasks, terminating the executor immediately.
36
Cont.
Example
static class MyTask implements Runnable
import
{
java.util.concurrent.ExecutorService;
private final int taskId;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public MyTask(int taskId) {
public static void main(String[] args)
this.taskId = taskId;
{
}
// Create a fixed-size thread pool
with 5 threads
@Override
ExecutorService executor =
public void run() {
Executors.newFixedThreadPool(5);
System.out.println("Task " +
taskId + " is being executed by " +
// Submit tasks for execution
Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
//0 is
Task Perform the by
being executed task's logic
pool-1-thread-1
Runnable task = new MyTask(i);
here Task 1 is being executed by pool-1-thread-2
executor.execute(task);
} Task 2 is being executed by pool-1-thread-3
}
} Task 3 is being executed by pool-1-thread-4
} Task 4 is being executed by pool-1-thread-5
// Shutdown the executor after
tasks are completed Task 5 is being executed by pool-1-thread-1
37
Advantages of Thread Pooling and
Executors
Efficient resource utilization: Thread pooling allows for reusing threads, reducing the
overhead of thread creation and destruction.
Scalability: Thread pools can manage a large number of tasks efficiently, ensuring
optimal utilization of available threads.
Load management: Executors provide mechanisms for controlling the number of
concurrent threads and managing task queues, allowing for load balancing and
preventing resource exhaustion.
Simplified concurrency management: Executors abstract away the complexities of
thread creation and management, providing a higher-level API for submitting and
managing tasks.
Error and exception handling: Executors provide mechanisms for handling errors,
exceptions, and task cancellation, simplifying error management in concurrent
applications.
38
Thread Groups
Thread groups provide a way to organize and manage threads as a unit.
They offer a hierarchical structure for grouping related threads together. Here are
some key points about thread groups:
Thread Hierarchy:
Thread groups form a hierarchical structure where thread groups can contain other thread groups,
forming a parent-child relationship.
This hierarchical organization allows for convenient management of threads.
Common Properties:
Thread groups typically share common properties and behaviors. For example, thread groups
can have a maximum priority, which is inherited by its child threads unless explicitly set otherwise.
Exception Handling:
Thread groups can define an uncaught exception handler that will be invoked when an exception
propagates up the thread hierarchy and is not caught by any thread in the group.
Monitoring and Control:
Thread groups provide facilities for monitoring and controlling the threads within them. For
example, you can enumerate the threads in a group, interrupt all threads in a group, or set the
daemon status for all threads in a group.
Security and Resource Management:
Thread groups can be used to enforce security policies and resource limits. By grouping threads 39
Cont.
Security and Resource Management:
Thread groups can be used to enforce security policies and resource limits.
By grouping threads together, you can apply restrictions and permissions at the group level,
ensuring better control over thread behavior and resource consumption.
public class ThreadGroupExample {
public static void main(String[] args)
{
ThreadGroup parentGroup = new
ThreadGroup("Parent Group");
// Create threads and assign them
to the parent group
Thread thread1 = new
Thread(parentGroup, new MyRunnable(),
"Thread 1");
Thread thread2 = new
Thread(parentGroup, new MyRunnable(),
"Thread 2");
Thread thread3 = new
Thread(parentGroup, new MyRunnable(),
"Thread 3"); 40
Cont.
// Start the threads try {
thread1.start(); Thread.sleep(1000);
thread2.start(); } catch (InterruptedException e)
thread3.start(); {
// Display information about the parent e.printStackTrace();
group }
parentGroup.list(); }
} }
static class MyRunnable implements Output
Runnable { Thread 1 is running.
@Override Thread 2 is running.
public void run() { Thread 3 is running
System.out.println(Thread.currentThread()
.getName() + " is running.");
}
41