Welcome back to our journey through Java programming! Today, we're diving into one of the most powerful features of Java: Multithreading. Multithreading allows your Java applications to execute multiple threads concurrently, making them more efficient and responsive.
Multithreading is the ability of a CPU or a single core in a multi-core processor to provide multiple threads of execution concurrently. In Java, a thread is the smallest unit of execution, and multithreading allows you to perform multiple tasks simultaneously.
Multithreading is a Java feature that allows concurrent execution of two or more parts of a program for maximum utilization of CPU. Each part of such program is called a thread. So, threads are light-weight processes within a process.
Every Java thread has a priority that helps the operating system determine the order in which threads are scheduled.
Java thread priorities are in the range between MIN_PRIORITY (a constant of 1) and MAX_PRIORITY (a constant of 10). By default, every thread is given priority NORM_PRIORITY (a constant of 5).
Thread States and Lifecycle
Threads in Java have different states, and they go through various stages in their lifecycle. The main thread states are:
New: The thread has been created but not yet started. Runnable: The thread is ready to run and waiting for CPU time. Running: The thread is waiting for a monitor lock to enter a synchronized block/method. Waiting: The thread is waiting for some condition to be satisfied. Timed Waiting: The thread is waiting for a specific period. Terminated: The thread has finished its execution. You can transition between these states using methods like start(), join(), sleep(), and wait().
Create a Thread by Implementing a Runnable Interface
In Java, there are two ways to create threads: by extending the Thread class or by implementing the Runnable interface. Here's an example of both approaches:
If your class is intended to be executed as a thread then you can achieve this by implementing a Runnable interface. You will need to follow three basic steps −
Step 1
As a first step, you need to implement a run() method provided by a Runnable interface. This method provides an entry point for the thread and you will put your complete business logic inside this method. Following is a simple syntax of the run() method −
Step 2
As a second step, you will instantiate a Thread object using the following constructor −
Thread(Runnable threadObj, String threadName);
Where, threadObj is an instance of a class that implements the Runnable interface and threadName is the name given to the new thread.
Step 3
Once a Thread object is created, you can start it by calling start() method, which executes a call to run( ) method. Following is a simple syntax of start() method −
Example
Here is an example that creates a new thread and starts running it −
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name
) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
Output
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-2, 1
Thread: Thread-1, 1
Thread Thread-2 exiting.
Thread Thread-1 exiting.
Two methods Creating Threads
In Java, there are two ways to create threads: by extending the Thread class or by implementing the Runnable interface. Here's an example of both approaches:
javaCopy code
// Extending Thread class
class MyThread extends Thread {
public void run() {
// Code to be executed in this thread
}
}
// Implementing Runnable interface
class MyRunnable implements Runnable {
public void run() {
// Code to be executed in this thread
}
}
// Creating and starting threads
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyRunnable runnable = new MyRunnable();
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}
Thread Methods
Following is the list of important methods available in the Thread class.
Synchronization
When multiple threads access shared resources concurrently, synchronization is crucial to prevent data corruption. You can use the synchronized keyword to create synchronized blocks or methods. Here's an example:
javaCopy code
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
Some Thread Example
public class day12 implements Runnable{
private int number;
public day12(int number) {
this.number=number;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 1; i <= 10; i++) {
System.out.printf("%s: %d * %d = %d\n", Thread.currentThread().getName(),
number, i, i * number);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("I will print table of 1 to 5 ");
for (int i = 1; i <= 5; i++) {
day12 mul = new day12(i);
Thread thread = new Thread(mul);
thread.start();
}
}
}
Dead Lock
class ThreadDeadLock
{
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[])
{
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread
{
public void run()
{
synchronized (Lock1)
{
System.out.println("Thread 1: Holding lock 1...");
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2)
{
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread
{
public void run()
{
synchronized (Lock2)
{
System.out.println("Thread 2: Holding lock 2...");
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1)
{
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
Exercise Questions
What is multithreading, and why is it important in Java programming? Explain the difference between extending the Thread class and implementing the Runnable interface to create threads. What is synchronization in multithreading, and why is it necessary? Can you name the various thread states in Java and describe when a thread transitions into each state? How can you ensure that only one thread at a time can execute a particular block of code or method in Java? Exercise Answers
Multithreading is the concurrent execution of multiple threads in a Java application, allowing it to perform multiple tasks simultaneously. It is essential for improving program efficiency and responsiveness. Extending the Thread class involves creating a new class that directly inherits from Thread, while implementing the Runnable interface allows a class to be executed by a thread. The latter is often preferred because it promotes better code separation and reusability. Synchronization in multithreading is a technique used to ensure that only one thread can access a shared resource or critical section at a time. It is necessary to prevent data corruption and race conditions. The thread states in Java are: New, Runnable, Blocked, Waiting, Timed Waiting, and Terminated. Threads transition between these states depending on their activities and method calls. You can ensure that only one thread at a time executes a block of code or method by using the synchronized keyword. This keyword can be applied to methods or blocks of code to create synchronized sections that only one thread can enter at a time. That's it for today's exploration of multithreading in Java! Multithreading is a complex topic with many nuances, so be sure to practice and experiment with it to gain a deeper understanding. Stay tuned for more Java programming adventures in the coming days!