Table of contents
- Introduction to Chapter 38
- Syllabus Overview
- Multitasking in Computers
- Types of Multitasking
- Process-Based Multitasking:
- Key Characteristics
- Real-Life Examples
- OS-Level Role
- Mechanisms
- Advantages
- Disadvantages
- Visualization
- Summary
- 2.Thread-based Multi-tasking in Java
- How Java Handles Multi-threading
- Thread Basics
- Multi-threading Execution
- Advantages of Multi-threading
- Thread APIs in Java
- Comparison: Single-threading vs. Multi-threading
- 1. Response Time
- 2. Execution of Tasks
- 3. Key Concepts
- 4. Multitasking at OS Level vs. Thread Level
- 5. Multithreading in Java
- 6. Challenges in Multithreading
- 7. Context Switching in Multithreaded Applications
- 8. Agenda of Multitasking
- 9. Example Allocation by the OS
- 4. What is a Thread?
- When is Multithreading Useful?
- 5. How to Define, Instantiate, and Start a Thread?
- JVM Internals for Thread Execution
- Multi-Threading Behavior
- Behind the Scenes
- Agenda of Multithreading
- Summary of Key Concepts
- Thread Scheduler in Java
- Thread Scheduling with start() Method
- Difference Between t.start() and t.run()
- Why start() Method is the Heart of Multi-threading
- Case Study: Improper Use of run() Method
- Conclusion
- Summary
Introduction to Chapter 38
Welcome to Chapter 38 of the Full Stack Java Development Series, where we delve into one of the most powerful features of Java: Multithreading. This chapter lays the foundation for understanding how Java enables concurrent execution of tasks, enhancing the responsiveness and performance of applications. By the end of this chapter, you'll have a solid understanding of the theoretical and practical aspects of multithreading, preparing you to write efficient, concurrent programs in Java.
Syllabus Overview
The topics we'll cover in this chapter are:
Introduction to Multithreading
What is Multithreading?
Types of Multitasking
Process-Based Multitasking
Thread-Based Multitasking
Defining and Starting Threads
Extending the
Thread
classImplementing the
Runnable
interface
Thread Management
Thread constructors
Setting thread priorities
Naming threads
Controlling Thread Execution
- Methods like
yield()
,join()
, andsleep()
- Methods like
Synchronization in Threads
Inter-Thread Communication
Understanding Deadlock
Daemon Threads
Advanced Topics
Thread Suspension and Resumption
Thread Groups
Green Threads
Thread Local Variables
Thread Lifecycle
Multitasking in Computers
Before diving into threads, let's explore the broader concept of multitasking:
What is Multitasking?
Multitasking refers to executing multiple tasks simultaneously. This is made possible by the Operating System (OS), which manages multiple applications and tasks in the computer's RAM. For instance:
Typing a Java program while listening to music and downloading a file.
Watching a YouTube video while editing documents and attending a Zoom call.
The OS enables these simultaneous activities by efficiently managing system resources, ensuring tasks don’t interfere with each other.
Types of Multitasking
Process-Based Multitasking:
Definition
Process-based multitasking refers to the execution of multiple tasks simultaneously, where each task runs as an independent process. The operating system (OS) handles the management and execution of these processes, ensuring smooth transitions between them.
Key Characteristics
Independence:
Each task operates as a separate process, isolated from others. They don't share memory unless explicitly designed to do so.Context Switching:
The OS facilitates transitions between processes through context switching.
This involves saving the state of the current process and loading the state of the next.
While effective, context switching can create a computational burden on the OS.
Heavyweight Operation:
Processes require more resources like memory and CPU time.
High resource demand can slow down the system, especially with limited RAM.
Real-Life Examples
Typing a Java program in a text editor.
Listening to music on an MP3 player.
Downloading a file from the internet.
Browsing the web using a browser.
OS-Level Role
The OS is pivotal in managing process-based multitasking.
It enables users to switch seamlessly between applications (e.g., from a text editor to a music player).
All processes run on RAM, coordinated by the OS.
Mechanisms
1. Context Switching
Definition: Transitioning the CPU from executing one process to another.
Purpose: Ensures smooth user experience without disruptions.
Process:
Saves the current process state.
Loads the state of the next process.
Impact:
Utilizes hardware components like CPU registers and RAM.
Imposes a computational load, making it a "heavyweight" operation.
2. Resource Management
Heavyweight multitasking requires the OS to manage RAM effectively.
Overloaded systems may experience slowdowns or crashes.
Advantages
True Parallelism: Independent tasks run concurrently.
OS Management: The operating system handles complexities, abstracting it from the user.
User-Friendly Navigation: Seamless switching between applications enhances productivity.
Disadvantages
High Resource Consumption: Requires significant memory and CPU resources.
System Lag: Overburdened systems may experience reduced performance.
Complex Implementation: Context switching increases the complexity of OS design.
Visualization
Consider three processes:
Process 1: Editing code in EditPlus.
Process 2: Watching a video on YouTube.
Process 3: Listening to an MP3.
All these processes coexist in RAM, switching control as directed by the OS.
Summary
Process-based multitasking is fundamental to modern computing, allowing multiple independent processes to run concurrently. While resource-intensive, it is essential for OS-level multitasking, making it a cornerstone of efficient system design and user experience.
2.Thread-based Multi-tasking in Java
What is Thread-based Multi-tasking?
Executing several tasks simultaneously, where each task is a separate independent part of the same program, is called Thread-based Multi-tasking.
Each independent part is technically referred to as a thread.
Java provides inbuilt support for working with threads through APIs such as
Thread
,Runnable
,ThreadGroup
,ThreadLocal
, and others.
Advantages of Thread-based Multi-tasking
Reduced Response Time: Multi-tasking helps decrease the waiting time of tasks, improving application performance.
Improved Performance: Assigning threads to tasks ensures efficient CPU utilization.
Key Application Areas
Thread-based multi-tasking is widely used in:
Developing multimedia graphics.
Building web application servers (a concept learned in JEE).
Designing video games.
Creating animations.
How Java Handles Multi-threading
Developer Effort: Java developers write only 10% of the logic for multi-threading.
The remaining 90% is managed by Java's API, making multi-threading implementation easier.Execution Management:
Writing a few lines of code allows developers to handle different tasks in different ways.
Java's JVM (Java Virtual Machine) manages all task execution.
Thread Basics
Thread
A thread is technically referred to as a line of execution.
Threads can either execute tasks sequentially (single-threading) or simultaneously (multi-threading).
Single-threaded Execution
In single-threaded execution:
Execution starts at Task-1.
Tasks 2 and 3 wait until Task-1 finishes.
This increases waiting time, which decreases the performance of the application.
Characteristics:
There is only one line of execution, referred to as a single thread.
Sequential execution results in increased waiting time and reduced performance.
Example Code:
// Single-threaded execution
public class SingleThreadExample {
public static void main(String[] args) {
System.out.println("Task-1 is executing...");
System.out.println("Task-2 is executing...");
System.out.println("Task-3 is executing...");
}
}
In this example:
- Task-2 and Task-3 will only execute after Task-1 completes.
Disadvantages:
Waiting time increases significantly.
Performance decreases due to the sequential execution of tasks.
Multi-threading Execution
How Does Multi-threading Work?
Tasks are categorized to ensure they are independent of each other.
For instance:- Task-1, Task-2, and Task-3 are independent tasks.
Each independent task is assigned its own line of execution (thread).
Steps to Enable Multi-threading
Categorize Tasks: Ensure each task is independent.
Create Threads: Assign a separate thread to each task.
Example Code:
// Multi-threaded execution
public class MultiThreadExample {
public static void main(String[] args) {
Thread task1 = new Thread(() -> System.out.println("Task-1 is executing..."));
Thread task2 = new Thread(() -> System.out.println("Task-2 is executing..."));
Thread task3 = new Thread(() -> System.out.println("Task-3 is executing..."));
task1.start();
task2.start();
task3.start();
}
}
Here:
Each task runs on its independent thread, allowing for simultaneous execution.
Waiting time is reduced, and performance improves.
Lightweight Nature of Threads
In Java, JVM manages threads, making them lightweight.
JVM handles switching between threads at the thread level, not at the memory level.
Since this operation is not directly controlled by hardware, threads are considered lightweight.
Advantages of Multi-threading
Effective CPU Utilization:
By promoting multi-tasking, CPU time is utilized effectively.Improved Performance:
The response time of applications is reduced, enhancing performance.Context Switching:
In thread-based applications, JVM handles context switching with the support of a thread scheduler.
In process-based applications, context switching is managed by the Operating System (OS).
Thread APIs in Java
Java makes multi-threading development simple and efficient. Here's why:
Java provides extensive API support for threads:
- APIs like
Thread
,Runnable
, andThreadLocal
are part of thejava.lang
package.
- APIs like
API Structure:
The
java.lang
package contains.class
files for multi-threading.Developers focus on defining what tasks threads need to perform (10% of the logic), while the API handles the rest.
Real-world Applications
Multi-threading is commonly used in:
Gaming Applications: For rendering and user interaction in real time.
Zoom Meetings: For managing video, audio, and chat streams simultaneously.
Comparison: Single-threading vs. Multi-threading
Aspect | Single-threading | Multi-threading |
Execution | Sequential (Task-1 → Task-2 → Task-3) | Parallel (Task-1, Task-2, Task-3 run simultaneously) |
Performance | Decreases with waiting time | Increases with independent task execution |
Control | Single line of execution | Multiple lines of execution |
Waiting Time | High | Low |
Management | JVM manages a single thread | JVM manages multiple threads |
1. Response Time
The processor (CPU) is the central control of any computer action.
Hardware devices, such as RAM, hard disks, and CPUs, are made operational by the Operating System (OS).
The OS is responsible for deciding how much time is allocated to every process.
2. Execution of Tasks
System Workflow Overview:
OS manages interactions between Hard Disk, RAM, and Microprocessor (CPU).
- Files like
.java
,.mp3
, or.mp4
are loaded into RAM before being executed by the CPU.
- Files like
Task-1: Executing Java Code
Java code is loaded into RAM, and this process is termed as loading.
The JVM (Java Virtual Machine) handles execution under CPU's guidance.
CPU spends time processing instructions as directed by the OS.
For example, executing a Java program might take 3 minutes.
Task-2: Listening to Music
A music file (
.mp3
) is moved to RAM.CPU dedicates time for its processing.
Example: 2 minutes allocated by the OS.
Task-3: Watching a Video
A video file (
.mp4
) is loaded into RAM upon opening.- CPU time: Example: 3 minutes allocated.
3. Key Concepts
Time Allocation: The OS decides how much time the CPU spends on each task.
Context Switching:
OS switches between tasks (processes) to ensure effective utilization of CPU time.
This switching is measured in clock cycles (Hz).
Multithreading:
- Using multithreading ensures tasks are completed efficiently, maximizing CPU time.
4. Multitasking at OS Level vs. Thread Level
OS Level: Context switching between processes (e.g., Java program, music, video).
Thread Level: Context switching between threads (smaller units of a process) is managed by the Thread Scheduler within the JVM.
- Example: In a 3-minute Java program, three tasks could be allocated 1 minute each.
5. Multithreading in Java
Java enables multithreading to maximize performance by using CPU time effectively.
90% of the multithreading logic is implemented in the Java API; developers only need to write the remaining 10%.
Java developers are responsible for:
Defining tasks for the threads.
Creating threads for these tasks.
Using thread-related APIs effectively.
6. Challenges in Multithreading
Writing multithreaded code.
Defining tasks for threads.
Creating threads for individual tasks.
Using Java’s thread API correctly.
Ensuring CPU time is effectively utilized.
7. Context Switching in Multithreaded Applications
Process-based applications:
- Context switching is managed by the OS.
Thread-based applications:
- Context switching is managed by the JVM’s Thread Scheduler.
8. Agenda of Multitasking
- Improve CPU utilization and system performance.
9. Example Allocation by the OS
Total time allocated: 3 minutes.
Thread-1: Executes Task 1 for 1 minute.
Thread-2: Executes Task 2 for 1 minute.
Thread-3: Executes Task 3 for 1 minute.
4. What is a Thread?
A thread represents a separate flow of execution within a program. If a program executes in a single flow, it is referred to as Single-Threaded Programming. However, a thread allows multiple independent flows of execution, enabling Multithreading.
Thread Characteristics:
Each thread performs a separate task (job).
In Java, threads can be created in two ways:
By implementing the
Runnable
interface.By extending the
Thread
class.
When is Multithreading Useful?
Multithreading is applicable when tasks are independent and can run concurrently. For instance, when there are two or more tasks that do not depend on each other, multithreading improves CPU utilization and program efficiency.
5. How to Define, Instantiate, and Start a Thread?
a. Extending the Thread
Class
To create a thread by extending the
Thread
class:Identify the Task: Define the work a thread needs to perform inside a
run()
method.Create a User-Defined Class: Extend the
Thread
class in this class.Override the
run()
Method: Define the thread's specific job inside this method.
Code Example:
class MyThread extends Thread { // Define a thread by extending Thread class.
@Override
public void run() { // Task of the thread.
for (int i = 1; i <= 10; i++) {
System.out.println("Child Thread");
}
}
}
public class MultithreadingExample {
public static void main(String[] args) {
MyThread t = new MyThread(); // Thread instantiation.
t.start(); // Start the thread (calls run() internally).
// Main thread's task.
for (int i = 1; i <= 10; i++) {
System.out.println("Main Thread");
}
}
}
Explanation of Execution:
Line 1:
MyThread t = new MyThread();
The
MyThread
object is created in the heap area.The
.class
file forMyThread
is loaded into the Method Area.
Line 2:
t.start();
The
start()
method in theThread
class is invoked.A new thread is created and scheduled for execution.
The
run()
method is called to perform the thread's task.
Main Thread:
The main thread executes tasks in the
main()
method.At Line 3, two threads (main and child) start competing for CPU time.
JVM Internals for Thread Execution
Method Area:
The
.class
files (Test.class
,MyThread.class
) are loaded into the method area.The JVM identifies the
main()
method inTest.class
.
Stack Area:
The main thread is automatically created by the JVM.
The
main()
method is loaded into the stack area for execution.
Heap Area:
- Objects, such as
MyThread t
, are created in the heap.
- Objects, such as
Thread Scheduler:
The thread scheduler, implemented as part of the JVM, decides which thread gets CPU time based on an algorithm.
The scheduling algorithm is vendor-specific and confidential.
Multi-Threading Behavior
Task Execution:
The main thread executes the logic inside the
main()
method.The child thread executes the logic inside the
run()
method.
Concurrency:
Threads execute independently, competing for CPU time.
The exact output depends on the thread scheduler.
Sample Outputs:
Execution 1:
Main Thread Main Thread Main Thread Main Thread Main Thread Child Thread Child Thread Child Thread Main Thread ...
Execution 2:
Main Thread Main Thread Child Thread Child Thread Child Thread Main Thread Main Thread ...
Behind the Scenes
Main Thread:
- Automatically created by the JVM to execute the
main()
method.
- Automatically created by the JVM to execute the
Child Thread:
Created explicitly using the
Thread
class orRunnable
interface.Execution begins when the
start()
method is invoked.
Agenda of Multithreading
The primary purpose of multithreading is to use CPU resources efficiently by running multiple independent tasks concurrently. This improves the overall performance of the program.
Thread Scheduler:
A software component responsible for managing threads' CPU time.
Uses confidential algorithms provided by JDK vendors.
Summary of Key Concepts
Defining a Thread:
Write a class that extends
Thread
.Override the
run()
method to define the thread's task.
Starting a Thread:
Instantiate the thread class.
Call the
start()
method to begin execution.
Thread Independence:
- Threads run independently, and their execution order is determined by the thread scheduler.
Output:
- Due to context switching, outputs may vary across executions but demonstrate concurrent task execution.
By using threads effectively, we can achieve improved performance and responsiveness in Java applications.
Thread Scheduler in Java
Overview of Thread Scheduler
The Thread Scheduler is a component of the JVM (Java Virtual Machine) responsible for deciding which thread will execute first when multiple threads are waiting to execute.
The exact order of thread execution cannot be predicted; only possible outcomes can be anticipated.
In multi-threading, the priority is on execution of threads rather than their execution order, ensuring improved performance.
The Thread Scheduler handles the execution of threads based on internal scheduling policies, which may vary across JVM implementations.
Thread Scheduling with start()
Method
Creating a New Thread
When the start()
method of a thread object is invoked, it:
Registers the thread with the Thread Scheduler:
- The scheduler keeps track of the thread's existence.
Handles low-level mandatory activities:
- This includes memory-level operations essential for thread initialization.
Invokes the
run()
method:- The logic defined in the
run()
method of the thread class or its subclass is executed.
- The logic defined in the
Code Demonstration
class Thread {
public void start() {
// Register thread with the Thread Scheduler
// Perform low-level memory and setup activities
// Invoke the run() method
}
}
Key Points About start()
Method
The
start()
method is part of the Thread class and not the user-defined thread class (e.g.,MyThread
).If invoked, the
start()
method of the Thread class performs all necessary logic and ensures that the thread is ready to execute.After thread creation and registration, the
start()
method internally calls the overriddenrun()
method in the subclass.
Code Example
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Child Thread");
}
}
}
public class MultithreadingExample {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // Creates and registers a new thread, then executes the run() method
}
}
Difference Between t.start()
and t.run
()
Aspect | t.start() | t.run () |
Thread Creation | Creates a new thread. | Does not create a new thread. |
Execution | Executes run() on a new thread. | Executes run() on the main thread. |
Multi-threading Behavior | Implements multi-threading. | Single-threaded execution. |
Context Switching | Context switching may occur. | No context switching. |
Code Example
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Child Thread");
}
}
}
public class MultithreadingExample {
public static void main(String[] args) {
MyThread t = new MyThread();
t.run(); // Directly calls run() without creating a new thread
for (int i = 1; i <= 10; i++) {
System.out.println("Main Thread");
}
}
}
Output of the Above Code
Child Thread
Child Thread
...
Child Thread
Main Thread
Main Thread
...
Main Thread
The execution is sequential as there is no multi-threading.
The
t.run
()
method behaves like a regular method call, executed by the main thread.
Why start()
Method is the Heart of Multi-threading
Registers the Thread:
- The
start()
method ensures that the thread is registered with the scheduler, enabling its management and execution.
- The
Handles Low-Level Activities:
- It performs all necessary preparatory tasks, such as memory allocation and thread initialization.
Invokes the
run()
Method:- Without the
start()
method, therun()
method cannot be executed in a multi-threaded manner.
- Without the
If the start()
method is not executed:
The thread will not be registered with the Thread Scheduler.
Multi-threading will not occur, leading to inefficient CPU utilization.
Importance of start()
Method
The start()
method bridges the gap between thread creation and execution, making it an essential component of the multi-threading process in Java.
Case Study: Improper Use of run()
Method
If the run()
method is called directly instead of using start()
, the thread will behave like a regular method in the main thread.
Code
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Child Thread");
}
}
}
public class MultithreadingExample {
public static void main(String[] args) {
MyThread t = new MyThread();
t.run(); // Improper use: behaves like a normal method
for (int i = 1; i <= 10; i++) {
System.out.println("Main Thread");
}
}
}
Output
Child Thread
Child Thread
...
Child Thread
Main Thread
Main Thread
...
Main Thread
No multi-threading occurs.
Both
run()
and the main thread logic execute sequentially.
Conclusion
The Thread Scheduler is a core component that manages thread execution in a multi-threaded environment.
Using the
start()
method is essential for achieving true multi-threading, as it registers threads, prepares them for execution, and invokes therun()
method.Directly calling the
run()
method bypasses these processes, leading to single-threaded execution and defeating the purpose of multi-threading.
Summary
Java Multithreading simplifies multitasking by leveraging the Java API, enabling developers to focus only on the application's specific logic. The OS ensures efficient process switching, while the Thread Scheduler optimizes thread execution, ensuring CPU time is used effectively.
Multithreading thus aligns with the goal of maximizing CPU performance and improving overall system efficiency.
Thread-based Multi-tasking allows tasks to run simultaneously, reducing response time and improving application performance.
Single-threading involves sequential task execution, leading to increased waiting time.
Multi-threading assigns tasks to independent threads, enhancing efficiency and utilizing CPU time effectively.
With 90% of the work handled by Java APIs, multi-threading is both powerful and developer-friendly, especially for applications like gaming, multimedia, and video conferencing.
By the end of this chapter, you'll master multithreading, a critical skill for modern Java developers. Stay tuned for an in-depth exploration of each topic!