MULTITHREADING
Multitasking
Multitasking is a process of executing multiple tasks simultaneously. We use multitasking to utilize the
CPU. Multitasking can be achieved in two ways:
o Process-based Multitasking (Multiprocessing)
o Thread-based Multitasking (Multithreading)
1) Process-based Multitasking (Multiprocessing)
o Each process has an address in memory. In other words, each process allocates a separate
memory area.
o A process is heavyweight.
o Cost of communication between the process is high.
o Switching from one process to another requires some time for saving and loading registers,
memory maps, updating lists, etc.
2) Thread-based Multitasking (Multithreading)
o Threads share the same address space.
o A thread is lightweight.
o Cost of communication between the thread is low.
Definition of Multithreading:
Multithreading means multiple flows of control.
It is a conceptual paradigm for programming where a program can be divided into two or
more processes that can run in parallel.
Java as a Multi-threaded Language:
Java allows writing programs where multiple processes can be executed concurrently
within a single program.
Java threads are often referred to as "lightweight threads," meaning they run in the same
memory space.
Advantages of Multithreading:
1. Better Utilization of System Resources:
o Programs with multiple threads result in better utilization of system resources.
2. Handling Multiple Problems:
o Certain problems can be better solved using multiple threads.
o For example, a multi-threaded program can show animations, play music, display
documents, and download files from the network simultaneously.
Communication and Efficiency:
Java methods and threads run in the same memory space, facilitating easy
communication among themselves.
An object in one thread can call a method in another thread without any overhead from
the OS.
Threads
A thread is a lightweight sub-process, the smallest unit of processing.
Multiprocessing and multithreading, both are used to achieve multitasking.
Threads are independent. If there occurs exception in one thread, it doesn't
affect other threads. It uses a shared memory area.
Life Cycle of Thread
Threads in a multithreaded environment can transition between different thread
states in Java as they execute. The Java Thread class defines these states and
represents different stages in the lifecycle of a thread. The possible states include:
1. New: When a thread has just been created, it is in the “New” state. At this
point, the Thread is not yet scheduled for execution and has not started
running.
2. Runnable: A thread enters the “Runnable” state after invoking the start()
method. In this state, the Thread is eligible to be scheduled by the
operating system and can start executing its code.
3. Running state Example: When a thread executes its code inside the run()
method.Use case: A thread executing a complex computation or performing
a time-consuming task.
4. Blocked/Waiting: A thread can enter the “Blocked” or “Waiting” state
under certain circumstances. For example, if a thread is waiting for a lock to
be released by another thread, it goes into the “Blocked” state. Similarly, if
a thread waits for a specific condition to be satisfied, it enters the “Waiting”
state. In these states, the Thread is not actively executing its code and is
not eligible for scheduling.
5. Timed Waiting: Threads can also enter the “Timed Waiting” state, similar
to the “Waiting” state but with a time constraint. For instance, a thread can
enter the “Timed Waiting” state when it calls methods like Thread.sleep()
or Object.wait() with a specific timeout value. The Thread remains in this
specific state until the timeout expires or until it receives a notification
from another thread.
6. Terminated: The final state in the thread lifecycle is the “Terminated” state.
A thread enters this state when it completes its execution or when an
unhandled exception occurs within the Thread. Once a thread is
terminated, it cannot transition to any other state.
Example Of Thread
if the run() method of the Hello class is called first, it will complete its execution first, and then
only the run() method of the Hi class will start its execution.
// Java Program to illustrate Runnable Interface in threads
Commonly used Constructor
o Thread()
o Thread(String name)
o Thread(Runnable r)
o Thread(Runnable r,String name)
Using the Thread Class: Thread(String Name)
public class MyThread1
{
public static void main(String args[])
{
Thread t= new Thread("My first thread");
t.start();
String str = t.getName();
System.out.println(str);
Using the Thread Class: Thread(Runnable r, String name)
public class MyThread2 implements Runnable
{
public void run()
{
System.out.println("Now the thread is running ...");
}
public static void main(String argvs[])
{
Runnable r1 = new MyThread2();
Thread th1 = new Thread(r1, "My new thread");
th1.start();
String str = th1.getName();
System.out.println(str);
}
}
isDaemon() Method
A daemon thread is a low-priority thread in java which runs in the background and
mostly created by JVM for performing background tasks like Garbage
Collection(GC).
If no user thread is running then JVM can exit even if daemon threads are running.
The only purpose of a daemon thread is to serve user threads. The isDaemon()
method can be used to determine the thread is daemon thread or not.
Syntax: public boolean isDaemon()
Priority of a Thread (Thread Priority)
Each thread has a priority. Priorities are represented by a number between 1 and
10. In most cases, the thread scheduler schedules the threads according to their
priority (known as preemptive scheduling). But it is not guaranteed because it
depends on JVM specification that which scheduling it chooses. Note that not
only JVM a Java programmer can also assign the priorities of a thread explicitly in
a Java program.
Whenever we create a thread in Java, it always has some priority assigned to
it. Priority can either be given by JVM while creating the thread or it can be
given by the programmer explicitly.
Setter & Getter Method of Thread Priority
public final int getPriority(): The java.lang.Thread.getPriority() method returns
the priority of the given thread.
public final void setPriority(int newPriority): The java.lang.Thread.setPriority()
method updates or assign the priority of the thread to newPriority. The method
throws IllegalArgumentException if the value newPriority goes out of the range,
which is 1 (minimum) to 10 (maximum).
3 constants are defined in it namely as follows:
1. public static int NORM_PRIORITY
2. public static int MIN_PRIORITY
3. public static int MAX_PRIORITY
// Java Program to Illustrate Priorities in Multithreading
// via help of getPriority() and setPriority() method
Output
Synchronization
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.
When multiple threads are trying to access same resources at same time on
that situation it may provide some wrong output or corrupt data.
The synchronization is mainly used to
1. To prevent thread interference.
2. To prevent consistency problem.
Types of Synchronization in Java
Method-Level Synchronization: In method-level synchronization, the entire
method is marked as synchronized and can only be accessed by one thread at
a time and remaining all the thread will wait at method level. This is achieved
by acquiring a lock on the object associated with the method.
Syntax for making Java Synchronized Method:
Acess_modifiers synchronized return_type method_name (Method_Parameters) {
// Method Statements
}
Block-Level Synchronization: In block-level synchronization,the entire method
is not get synchronized only a portion of the method is marked as
synchronized. This is achieved by wrapping the portion of code that requires
synchronization within a synchronized block and acquiring a lock on a specified
object.
Syntax for writing Java Synchronized Block:
synchronized (object reference expression) {
//statements/methods
}
Both method-level and block-level synchronization can be used to control access
to shared resources in a multi-threaded environment, but block-level
synchronization provides more fine-grained control over the synchronization of a
program. Block-level synchronization allows multiple threads to access different
parts of a shared resource simultaneously, which can improve performance in
certain cases.
Static Synchronization
Output
Insufficient balance
Insufficient balance
Insufficient balance
Inter-Thread Communication or Co-operation
Inter-thread communication or Co-operation is all about allowing synchronized threads to
communicate with each other.
Cooperation (Inter-thread communication) is a mechanism in which a thread is paused
running in its critical section and another thread is allowed to enter (or lock) in the same
critical section to be executed
Inter-thread communication is important when you develop an application where two or
more threads exchange some information. Inter-thread communication is achieved by using
the wait(), notify(), and notifyAll() methods of the Object class.
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.
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.
3) notifyAll() method
Wakes up all threads that are waiting on this object's monitor.
Output: