In today's fast-paced digital world, efficiency and speed are crucial for any application or system. One way to achieve this is by utilizing multiple threads to perform tasks concurrently, a concept known as multithreading. In Java, multithreading is a powerful feature that allows developers to create efficient, responsive, and scalable applications. we'll delve into the world of Java multithreading, exploring its concepts, benefits, and best practices.
What is Multithreading? 🚀
Multithreading is a programming technique that enables a program to execute multiple threads or flows of execution concurrently, improving the overall performance and responsiveness of the application. Each thread runs in parallel, sharing the same memory space and resources, but with its own program counter, stack, and local variables.
Why Multithreading in Java? 👀
Java provides built-in support for multithreading, making it an ideal choice for developing concurrent applications. Here are some reasons why multithreading is essential in Java:
Improved Performance : By executing multiple threads simultaneously, programs can perform tasks faster and more efficiently.
Responsive UI : In GUI applications, multithreading ensures that the UI remains responsive while performing background tasks.
Better Resource Utilization : Threads can share resources and data, making better use of system resources.
Key Concepts in Java Multithreading 📝
To understand Java multithreading, it's essential to grasp the following concepts:
Thread : A thread is a single flow of execution that runs concurrently with other threads.
Thread Life Cycle : A thread goes through various stages, including creation, start, run, sleep, wait, notify, and termination.
Synchronization : Synchronization is the process of coordinating access to shared resources to prevent data corruption and ensure thread safety.
Thread Safety : Thread safety refers to the ability of a program to function correctly even when multiple threads access shared resources concurrently.
Deadlock : A deadlock occurs when two or more threads are blocked, waiting for each other to release a resource, resulting in a deadlock situation.
Creating Threads in Java ✨
There are two main ways to create a thread in Java:
- By Extending the Thread Class
- By Implementing the Runnable Interface
Extending the Thread Class 🤷♀️
To create a thread by extending the Thread class, you need to create a new class that extends Thread and override its run() method. let's understand throw example:
//MyThread.java
public class MyThread extends Thread{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
}
}
}
//main.java
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
output
24 Value: 0
25 Value: 0
24 Value: 1
25 Value: 1
24 Value: 2
25 Value: 2
24 Value: 3
25 Value: 3
24 Value: 4
25 Value: 4
24 Value: 5
25 Value: 5
25 Value: 6
24 Value: 6
25 Value: 7
24 Value: 7
24 Value: 8
24 Value: 9
25 Value: 8
25 Value: 9
Implementing the Runnable Interface 🤩
To create a thread by implementing the Runnable interface, you need to implement the run() method and pass an instance of your class to a Thread object. Here's an example:
//MyRunnable.java
public class MyRunnable implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getId() + " Value: " + i);
}
}
}
// main.java
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
}
}
output24 Value: 0
25 Value: 0
24 Value: 1
25 Value: 1
24 Value: 2
25 Value: 2
24 Value: 3
25 Value: 3
24 Value: 4
25 Value: 4
24 Value: 5
25 Value: 5
24 Value: 6
24 Value: 7
25 Value: 6
24 Value: 8
25 Value: 7
24 Value: 9
25 Value: 8
25 Value: 9
Thread Lifecycle 🎯
A thread in Java can be in one of the following states:-
New : When a thread is created but not yet started.
Runnable : When the thread is ready to run but waiting for CPU allocation.
Running : When the thread is running.
Blocked : When the thread is blocked and waiting for a monitor lock.
Waiting : When the thread is waiting indefinitely for another thread to perform a particular action.
Timed Waiting : When the thread is waiting for a specified amount of time.
Terminated : When the thread has finished its execution.
Thread Methods
Java provides several methods to manage threads:
start() : Starts the execution of the thread.
run() : Contains the code that defines the task of the thread.
sleep(long millis) : Causes the thread to sleep for the specified number of milliseconds.
join() : Waits for the thread to die.
yield() : Causes the currently executing thread to pause and allow other threads to execute.
synchronized : Ensures that only one thread can access the synchronized block of code at a time.
Example 📢
public class Main {
public static int counter1 = 0;
public static int counter2 = 0;
public static void main(String[] args) throws InterruptedException {
// thread1 created
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter1++;
}
}
});
thread1.start(); // thread1 started
//thread2 created
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter2++;
}
}
});
thread2.start(); // thread2 started
// thread 3 created
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Counter1: "+counter1);
System.out.println("Counter2: "+counter2);
}
});
// thread3 will start when thread1 and thread2 will end
thread3.start();
System.out.println("Thread "+Thread.currentThread().getName());
}
}
output
Thread main
Counter1: 10
Counter2: 10
Conclusion 🔎
Multithreading is an essential concept in Java that allows for the concurrent execution of two or more threads, improving the performance and responsiveness of applications. By understanding and utilizing the Thread class and Runnable interface, you can create and manage threads efficiently. Experiment with the examples provided and explore the various methods and states of threads to master multithreading in Java.