Multithreading in Java
Multithreading in Java
However, we use multithreading than multiprocessing because threads use a shared memory area.
They don't allocate separate memory area so saves memory,
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.
Process
A process is an instance of a running program. When you open an application (like a browser or text
editor), it creates a process in your computer's memory. Each process runs independently and has its
own memory and resources.
Thread
A thread is the smallest unit of a process that can execute code. A process can have multiple threads,
each performing a different task simultaneously within the same process memory
EXAMPLE
Process
Imagine Microsoft Word is a process. When you open the Microsoft Word application, your
computer creates a new process for it. This process is independent and has its own allocated
memory and resources, like the toolbar, fonts, and document editor.
Think of this process as the overall workspace where you’re working on a document.
Thread
Inside the Microsoft Word process, there are different threads handling separate tasks
simultaneously:
Typing Thread: One thread handles typing, making sure letters appear on the screen as you
type.
Spell Check Thread: Another thread continuously checks for spelling and grammar errors.
Auto-Save Thread: Another thread runs in the background to save your work automatically
at intervals.
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.
Ex:
Example of Multiprocessing
1. Stream music.
In a multiprocessing system, each of these tasks (streaming, downloading, and editing) could run
in separate processes on different CPU cores. This way, each process runs independently without
slowing down or interfering with each other, allowing you to multitask smoothly.
o A thread is lightweight.
he Thread Life Cycle in Java (or most programming languages) represents the different stages a
thread goes through from creation to termination. Here’s a breakdown of each stage with a simple
explanation:
Description: When a thread is created but hasn’t started yet, it’s in the New state.
2. Runnable
Description: When a thread’s start() method is called, it enters the Runnable state.
3. Blocked
Description: If a thread tries to enter a synchronized block but another thread holds the lock,
it enters the Blocked state.
4. Waiting
Description: A thread is in the Waiting state when it’s paused, waiting for some other thread
to perform a specific action (like notify it).
5. Timed Waiting
Description: Once a thread finishes executing (either by completing its task or due to an
unhandled exception), it enters the Terminated state.
Visual Summary
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.
3. System.out.println("thread is running...");
4. }
7. t1.start();
8. }
9. }
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.
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().
3. System.out.println("thread is running...");
4. }
5.
9. t1.start();
10. }
11. }
Note : A class implementing the Runnable interface does not execute on its own because the
Runnable interface only defines the task (in the run() method), but it doesn't have the capability to
manage or start the thread. You must pass the Runnable object to a Thread object, which is
responsible for managing and starting the thread.
Without creating a Thread object and calling its start() method, the task in the run() method will not
be executed, as the Thread class
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.
Thread.sleep()
The method sleep() is being used to halt the working of a thread for a given amount of time. The
time up to which the thread remains in the sleeping state is known as the sleeping time of the
thread. After the sleeping time is over, the thread starts its execution from where it has left.
No. After starting a thread, it can never be started again. If you does so,
an IllegalThreadStateException is thrown. In such case, thread will run once but for second time, it
will throw exception.
If you directly call the run() method in Java instead of the start() method, it will not start a new
thread. Instead, the run() method will execute on the current thread (the thread that calls it) like a
regular method, which defeats the purpose of using threads for concurrent execution.
join()
The join() method in Java is used to pause the execution of the current thread until the thread on
which join() is called has finished its execution. In simpler terms, it allows one thread to wait for
another thread to complete before continuing its execution.
Thread Synchronization: It ensures that one thread finishes its task before another thread
starts or continues.
Order of Execution: When you need to control the order of execution of threads (for
example, waiting for one thread to finish before proceeding with another.
@Override
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
// Starting threads
t1.start();
t2.start();
// Calling join() to make the main thread wait until t1 and t2 finish
Output:
plaintext
Copy code
Naming Thread
The Thread class provides methods to change and get the name of a thread. By default, each thread
has a name, i.e. thread-0, thread-1 and so on. By we can change the name of the thread by using the
setName() method. The syntax of setName() and getName() methods are given below:
Each thread has a priority. Priorities are represented by a number between 1 and 10.
Let's discuss the setter and getter method of the thread priority.
public final int getPriority(): The java.lang.Thread.getPriority() method returns the priority of the
given thread.
Default priority of a thread is 5 (NORM_PRIORITY). The value of MIN_PRIORITY is 1 and the value of
MAX_PRIORITY is 10.
Daemon thread in Java is a service provider thread that provides services to the user thread. Its life
depend on the mercy of user threads i.e. when all the user threads dies, JVM terminates this thread
automatically.
There are many java daemon threads running automatically e.g. gc, finalizer etc.
It provides services to user threads for background supporting tasks. It has no role in life than
to serve user threads.
The sole purpose of the daemon thread is that it provides services to user thread for background
supporting task. If there is no user thread, why should JVM keep running this thread. That is why JVM
terminates the daemon thread if there is no user thread.
The java.lang.Thread class provides two methods for java daemon thread.
File: MyThread.java
5. }
6. else{
8. }
9. }
14.
16.
18. t2.start();
19. t3.start();
20. }
21. }
Test it Now
Output:
Note: If you want to make a user thread as Daemon, it must not be started otherwise it will throw
IllegalThreadStateException.
1. t1.start();
Garbage Collection is process of reclaiming the runtime unused memory automatically. In other
words, it is a way to destroy the unused objects.
To do so, we were using free() function in C language and delete() in C++. But, in java it is performed
automatically. So, java provides better memory management.
o It makes java memory efficient because garbage collector removes the unreferenced objects
from heap memory.
o It is automatically done by the garbage collector(a part of JVM) so we don't need to make
extra efforts.
1) By nulling a reference:
2. e=null;
3) By anonymous object:
1. new Employee();
finalize() method
The finalize() method is invoked each time before the object is garbage collected. This method can be
used to perform cleanup processing. This method is defined in Object class as:
Note: The Garbage collector of JVM collects only those objects that are created by new keyword. So
if you have created any object without new, you can use finalize method to perform cleanup
processing (destroying remaining objects).
gc() method
The gc() method is used to invoke the garbage collector to perform cleanup processing. The gc() is
found in System and Runtime classes.
Note: Garbage collection is performed by a daemon thread called Garbage Collector(GC). This thread
calls the finalize() method before object is garbage collected.
Synchronization
In Java, synchronization is a technique to control the access of multiple threads to shared resources,
ensuring that only one thread can access a resource at a time. This prevents race conditions, where
multiple threads try to modify or access shared data concurrently, potentially causing unexpected
and incorrect outcomes.
When multiple threads access shared data simultaneously without proper control, they may:
Imagine a simple bank application where multiple people can withdraw money from the same
account. Let’s say two people, Alice and Bob, both try to withdraw money from the same account at
the same time. Without synchronization, they might both see the same initial balance and proceed
with their withdrawals, leading to an incorrect final balance.
Scenario:
2. Withdrawals: Alice and Bob both try to withdraw $80 at the same time.
Without Synchronization:
1. Step 1: Both Alice and Bob’s threads access the account at the same time and see the
balance is $100.
2. Step 2: Alice’s thread deducts $80, so she sees the new balance as $20.
3. Step 3: Bob’s thread, without knowing Alice already withdrew, also deducts $80, and he also
sees the new balance as $20.
4. Result: Both withdrawals go through, but the final balance should be -$60, not $20. This is
incorrect because both threads changed the balance at the same time.
Final Outcome:
By synchronizing the withdraw() method, we ensure only one thread accesses the account
balance at a time. This prevents both Alice and Bob from withdrawing at the same time and
ensures the balance is accurate after each transaction
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.
Mutual exclusion ensures that only one thread can access a critical section (a part of code that
modifies shared resources) at a time. This avoids errors when multiple threads try to access or
modify shared data simultaneously.
Imagine a shared bank account with two people, Alice and Bob, who both try to withdraw money at
the same time:
1. Without Mutual Exclusion: Alice and Bob both see the same initial balance, say $100. They
both withdraw $80 at the same time, so the balance shows $20 for both. This is a mistake
since two withdrawals of $80 should lead to a negative balance, but they both see $20
because they accessed the balance at the same time.
o When Alice’s thread starts a withdrawal, it locks access to the balance, so Bob’s
thread waits until Alice finishes.
o Alice withdraws $80, the balance is now $20, and then she releases the lock.
o Now, when Bob’s thread accesses the balance, he sees $20 and realizes he can’t
withdraw $80, so he doesn’t proceed.
In Java, synchronized methods and synchronized blocks are used to enforce mutual exclusion.
How it Works:
o If the store is not full, it adds the item and calls notify() to let the Consumer know an
item is available.
o If the store is full, it calls wait() to pause, so it stops adding items until there’s space.
o If the store has items, it removes one and calls notify() to let the Producer know
there’s space.
A synchronized method in Java is a method that can only be executed by one thread at a time. It is
used to control access to a shared resource (like a variable or an object) so that only one thread can
access it, preventing conflicts or errors when multiple threads try to access the same resource
simultaneously.
Simple Explanation:
Imagine two people, Alice and Bob, trying to use a printer at the same time. If both are allowed to
access the printer at once, they might interfere with each other (e.g., printing the wrong pages, or
the printer breaking).
Without synchronization: Both Alice and Bob can print at the same time, causing problems.
With synchronization: If Alice is using the printer, Bob has to wait until Alice is done. Once
Alice is finished, Bob can use it.
Example:
java
Copy code
class Printer {
// Synchronized method
// Simulate printing
try {
} catch (InterruptedException e) {
e.printStackTrace();
thread1.start();
thread2.start();
Synchronized block can be used to perform synchronization on any specific resource of the method.
Suppose we have 50 lines of code in our method, but we want to synchronize only 5 lines, in such
cases, we can use synchronized block.
If we put all the codes of the method in the synchronized block, it will work same as the synchronized
method.
Points to Remember
o A Java synchronized block doesn't allow more than one JVM, to provide access control to a
shared resource.
o The system performance may degrade because of the slower working of synchronized
keyword.
Static Synchronization
If you make any static method as synchronized, the lock will be on the class not on object.
Imagine you have two objects of a class, say Table. You create two objects, object1 and
object2, and have multiple threads (e.g., t1, t2, t3, t4) trying to access synchronized methods
in these objects.
If the synchronized method is part of an instance (like a regular method), each object
(object1 and object2) has its own lock. So:
o t1 and t2 can safely work on object1 because they share the same lock for that
object.
o t3 and t4 can safely work on object2 because they share the same lock for that
object.
Now, if you make the method static, the lock is placed on the class itself instead of each
individual object.
So, if multiple threads (e.g., t1, t2, t3, t4) try to access the static synchronized method, they
must share the same lock because it's now the class that holds the lock.
o This means even though t1 is working on object1 and t3 is working on object2, they
can't interfere with each other because they are both trying to acquire the class-
level lock.
o In this case, there’s no chance of interference, because the lock is shared across all
objects of that class.
Summary:
Without static synchronization, each object gets its own lock, so threads on different objects
can still interfere.
With static synchronization, all objects share a single lock on the class, preventing
interference even if threads work on different objects.
Deadlock in Java
Deadlock in Java is a part of multithreading. Deadlock can occur in a situation when a thread is
waiting for an object lock, that is acquired by another thread and second thread is waiting for an
object lock that is acquired by first thread. Since, both threads are waiting for each other to release
the lock, the condition is called deadlock.
Inter-thread communication is a mechanism that allows threads to communicate with each other
and coordinate their activities. In Java, this is typically achieved using the methods provided by the
Object class: wait(), notify(), and notifyAll(). These methods enable threads to signal each other to
proceed or wait, facilitating cooperation between threads.
Key Concepts:
1. Wait: A thread can pause its execution by calling the wait() method on an object. The thread
will remain in a waiting state until it is notified by another thread.
2. Notify: A thread can wake up one of the waiting threads by calling the notify() method on an
object.
3. NotifyAll: A thread can wake up all the waiting threads by calling the notifyAll() method on
an object.
Both threads must coordinate to avoid issues like overproducing or consuming from an
empty buffer.
Test.java
1. class Customer{
2. int amount=10000;
3.
5. System.out.println("going to withdraw...");
6.
7. if(this.amount<amount){
9. try{wait();}catch(Exception e){}
10. }
11. this.amount-=amount;
13. }
14.
17. this.amount+=amount;
19. notify();
20. }
21. }
22.
28. }.start();
31. }.start();
32.
33. }}
Output:
going to withdraw...
going to deposit...
deposit completed...
withdraw completed
Interrupting a Thread:
If any thread is in sleeping or waiting state (i.e. sleep() or wait() is invoked), calling the interrupt()
method on the thread, breaks out the sleeping or waiting state throwing InterruptedException. If the
thread is not in the sleeping or waiting state, calling the interrupt() method performs normal
behaviour and doesn't interrupt the thread but sets the interrupt flag to true.
interrupt(): Used to interrupt a specific thread, setting its interrupt flag to true. If the thread is
blocked, it will throw InterruptedException.
interrupted(): A static method that checks and clears the interrupt status of the current thread.
isInterrupted(): Checks the interrupt status of the thread on which it is called, without clearing the
interrupt flag.