Contents
Multithreading
Multithreading in Java
Multithreading is a key feature in Java that enables the concurrent execution of multiple sections of a program, maximizing CPU utilization. Each such section is referred to as a thread, which represents a lightweight process.
There are two primary ways to create threads in Java:
1. By extending the Thread
class
2. By implementing the Runnable
interface
Creating a Thread by Extending the Thread
Class:
In this approach, a class is created that extends the java.lang.Thread
class. The run()
method is overridden, as this is where the code for the new thread is executed. The thread is started by invoking the start()
method, which internally calls the run()
method.
Example:
// Java code for thread creation by extending the Thread class
class ThreadDemo extends Thread {
public void run() {
try {
// Displaying the ID of the thread that is running
System.out.println("Thread " + Thread.currentThread().getId() + " is active");
} catch (Exception e) {
// Handling any exception that occurs
System.out.println("An exception occurred");
}
}
}
// Main Class
public class ThreadExample {
public static void main(String[] args) {
int numberOfThreads = 5; // Defining the number of threads
for (int i = 0; i < numberOfThreads; i++) {
ThreadDemo threadInstance = new ThreadDemo();
threadInstance.start();
}
}
}
Output:
Thread 11 is active
Thread 13 is active
Thread 12 is active
Thread 15 is active
Thread 14 is active
Creating a Thread by Implementing the Runnable
Interface:
In this approach, a class implements the Runnable
interface, and the run()
method is provided for defining the thread’s behavior. A Thread
object is then created and passed an instance of the class implementing Runnable
, and the start()
method is called to execute the thread.
Example:
// Java code for thread creation by implementing the Runnable interface
class ThreadRunnableDemo implements Runnable {
public void run() {
try {
// Displaying the ID of the thread that is running
System.out.println("Thread " + Thread.currentThread().getId() + " is active");
} catch (Exception e) {
// Handling any exception that occurs
System.out.println("An exception occurred");
}
}
}
// Main Class
public class RunnableExample {
public static void main(String[] args) {
int numberOfThreads = 5; // Defining the number of threads
for (int i = 0; i < numberOfThreads; i++) {
Thread threadInstance = new Thread(new ThreadRunnableDemo());
threadInstance.start();
}
}
}
A thread in Java can exist in one of several states at any given time. A thread can only be in one of these states at any particular moment. The primary states of a thread are:
1. New State
2. Runnable State
3. Blocked State
4. Waiting State
5. Timed Waiting State
6. Terminated State
The diagram below illustrates the various states a thread can be in during its lifecycle
Life Cycle of a Thread:
The lifecycle of a thread can be broken down into different states, which are explained below:
- New State: A thread is in this state when it has been created but not yet started. The thread’s code is still waiting to be executed.
- Runnable State: When a thread is ready to run, it moves into the runnable state. In this state, it may be actively running or waiting to be allocated CPU time by the thread scheduler.
- Blocked State: A thread enters this state when it attempts to acquire a lock that is held by another thread. The thread moves back to the runnable state once it obtains the lock.
- Waiting State: A thread moves into the waiting state when it calls
wait()
orjoin()
. It remains in this state until it is notified or the thread it is waiting for completes its execution. - Timed Waiting State: When a thread is waiting for a specified amount of time (e.g., using
sleep()
orwait()
with a timeout), it enters the timed waiting state. - Terminated State: A thread enters this state after its execution has completed or when an error occurs that causes it to terminate prematurely.
Java Enum Constants for Thread States:
In Java, the Thread.State
class provides constants representing these thread states:
1. NEW
: A thread that has not yet started.
public static final Thread.State NEW
2. RUNNABLE
: A thread that is executing in the JVM but may still be waiting for system resources such as the CPU.
public static final Thread.State RUNNABLE
3. BLOCKED
: A thread that is waiting for a monitor lock to enter a synchronized block or method.
public static final Thread.State BLOCKED
4. WAITING
: A thread that is waiting indefinitely for another thread to perform a particular action.
public static final Thread.State WAITING
5. TIMED_WAITING
: A thread that is waiting for a specified period of time.
public static final Thread.State TIMED_WAITING
6. TERMINATED
: A thread that has finished its execution.
public static final Thread.State TERMINATED
Example Demonstrating Thread States:
// Java program to demonstrate thread states
class MyThread implements Runnable {
public void run() {
// Simulating a thread going into timed waiting state
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("State of thread1 after calling join() on thread2 - "
+ ThreadExample.thread1.getState());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadExample implements Runnable {
public static Thread thread1;
public static ThreadExample obj;
public static void main(String[] args) {
obj = new ThreadExample();
thread1 = new Thread(obj);
// thread1 is in NEW state
System.out.println("State of thread1 after creating it - " + thread1.getState());
thread1.start();
// thread1 is now in RUNNABLE state
System.out.println("State of thread1 after calling .start() method - " + thread1.getState());
}
public void run() {
MyThread task = new MyThread();
Thread thread2 = new Thread(task);
// thread2 is in NEW state
System.out.println("State of thread2 after creating it - " + thread2.getState());
thread2.start();
// thread2 is now in RUNNABLE state
System.out.println("State of thread2 after calling .start() method - " + thread2.getState());
// Simulating a timed waiting state
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("State of thread2 after calling .sleep() method - " + thread2.getState());
try {
// thread1 waits for thread2 to finish
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("State of thread2 after it has completed execution - " + thread2.getState());
}
}
Output:
State of thread1 after creating it - NEW
State of thread1 after calling .start() method - RUNNABLE
State of thread2 after creating it - NEW
State of thread2 after calling .start() method - RUNNABLE
State of thread2 after calling .sleep() method - TIMED_WAITING
State of thread1 after calling join() on thread2 - WAITING
State of thread2 after it has completed execution - TERMINATED
Java Thread Priority in Multithreading
Java operates in a multithreaded environment, where the thread scheduler assigns CPU time to threads based on their priority. When a thread is created, it is always assigned a priority, either by the JVM or explicitly by the programmer.
Thread priority in Java is represented by a number between 1 and 10. The default priority is 5, while the minimum priority is 1, and the maximum priority is 10. Java provides three constants for priority levels:
public static int NORM_PRIORITY
(Default value of 5)public static int MIN_PRIORITY
(Minimum value of 1)public static int MAX_PRIORITY
(Maximum value of 10)
We can use the getPriority()
method to retrieve the current priority of a thread and the setPriority(int newPriority)
method to assign a new priority to a thread. The setPriority()
method throws an IllegalArgumentException
if the provided value is outside the valid range (1 to 10).
Example: Getting and Setting Thread Priorities
// Java Program to demonstrate thread priorities
class PriorityExample extends Thread {
// Overriding the run method to display a message
public void run() {
System.out.println("Inside run method of thread: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
// Creating three threads
PriorityExample t1 = new PriorityExample();
PriorityExample t2 = new PriorityExample();
PriorityExample t3 = new PriorityExample();
// Displaying the default priority of threads
System.out.println("t1 priority: " + t1.getPriority());
System.out.println("t2 priority: " + t2.getPriority());
System.out.println("t3 priority: " + t3.getPriority());
// Changing the priority of the threads
t1.setPriority(3);
t2.setPriority(7);
t3.setPriority(10);
// Displaying the updated priority of threads
System.out.println("Updated t1 priority: " + t1.getPriority());
System.out.println("Updated t2 priority: " + t2.getPriority());
System.out.println("Updated t3 priority: " + t3.getPriority());
// Displaying the main thread's priority
System.out.println("Main thread priority: " + Thread.currentThread().getPriority());
// Setting the main thread's priority to 9
Thread.currentThread().setPriority(9);
System.out.println("Updated main thread priority: " + Thread.currentThread().getPriority());
}
}
Output:
t1 priority: 5
t2 priority: 5
t3 priority: 5
Updated t1 priority: 3
Updated t2 priority: 7
Updated t3 priority: 10
Main thread priority: 5
Updated main thread priority: 9
Explanation:
- Initially, all threads (
t1
,t2
,t3
) have the default priority of 5. - After updating,
t1
has a priority of 3,t2
has 7, andt3
has 10. - The main thread’s priority starts at 5, but it is later updated to 9.
The thread scheduler prioritizes threads with higher priority values. For instance, in this example, t3
would have a higher chance of being executed before t2
and t1
, based on its priority. However, if two threads have the same priority, the order of execution is dependent on the scheduler’s algorithm, which could be round-robin or first-come, first-serve.
Example:
// Java program demonstrating child threads inheriting parent priority
class ChildThreadExample extends Thread {
// Overriding the run method
public void run() {
System.out.println("Inside run method of thread: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
// Setting main thread's priority to 6
Thread.currentThread().setPriority(6);
// Displaying the main thread's priority
System.out.println("Main thread priority: " + Thread.currentThread().getPriority());
// Creating a new thread
ChildThreadExample t1 = new ChildThreadExample();
// Displaying the child thread's priority (should inherit from main thread)
System.out.println("t1 thread priority: " + t1.getPriority());
}
}
Output:
Main thread priority: 6
t1 thread priority: 6
Main thread
Java provides native support for multithreaded programming, allowing multiple parts of a program to run concurrently. Each of these independent parts is known as a “thread,” with each thread following its own execution path.
When a Java application starts, the main thread is the first thread that begins execution. It is crucial for the overall program as it often spawns child threads and usually handles shutdown activities at the end.
Properties of the Main Thread:
1. It spawns other threads, often referred to as child threads.
2. It usually is the last thread to complete execution, as it might perform tasks like closing resources.
To manage the main thread, you need to get a reference to it using the currentThread()
method from the Thread
class. By default, the main thread has a priority of 5, and its child threads will inherit this priority.
Example: Controlling the Main Thread
// Java program to demonstrate controlling the Main Thread
public class MainThreadControl extends Thread {
public static void main(String[] args) {
// Get reference to the main thread
Thread mainThread = Thread.currentThread();
// Display main thread name
System.out.println("Current thread: " + mainThread.getName());
// Change the name of the main thread
mainThread.setName("PrimaryThread");
System.out.println("After name change: " + mainThread.getName());
// Display the main thread's priority
System.out.println("Main thread priority: " + mainThread.getPriority());
// Set the main thread's priority to maximum (10)
mainThread.setPriority(Thread.MAX_PRIORITY);
System.out.println("Main thread new priority: " + mainThread.getPriority());
// Print a message five times from the main thread
for (int i = 0; i < 5; i++) {
System.out.println("Main thread iteration: " + i);
}
// Create a child thread within the main thread
Thread childThread = new Thread() {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Child thread iteration: " + i);
}
}
};
// Display the child thread's priority (inherited from the main thread)
System.out.println("Child thread priority: " + childThread.getPriority());
// Change the child thread's priority to the minimum (1)
childThread.setPriority(Thread.MIN_PRIORITY);
System.out.println("Child thread new priority: " + childThread.getPriority());
// Start the child thread
childThread.start();
}
}
Output:
Current thread: main
After name change: PrimaryThread
Main thread priority: 5
Main thread new priority: 10
Main thread iteration: 0
Main thread iteration: 1
Main thread iteration: 2
Main thread iteration: 3
Main thread iteration: 4
Child thread priority: 10
Child thread new priority: 1
Child thread iteration: 0
Child thread iteration: 1
Child thread iteration: 2
Child thread iteration: 3
Child thread iteration: 4
Explanation:
- The main thread’s default name is
main
, but it’s renamed toPrimaryThread
. - The default priority of the main thread is 5, but it’s changed to 10 (maximum).
- The child thread inherits the main thread’s priority initially but is later reduced to 1.
Example: Deadlock Using the Main Thread
A deadlock can be created with a single thread by using the join()
method on the main thread itself, causing it to wait indefinitely for itself to finish, which results in a deadlock.
// Java program to demonstrate deadlock using the Main thread
public class DeadlockExample {
public static void main(String[] args) {
try {
// Print statement indicating deadlock entry
System.out.println("Entering Deadlock");
// Deadlock: The main thread waits for itself to complete
Thread.currentThread().join();
// This statement will never be reached
System.out.println("This will never be printed");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output:
Entering Deadlock
Java.lang.Thread Class in Java
A thread is a single sequence of execution within a program. Java allows a program to have multiple threads, each of which can be managed individually. Each thread is assigned a priority, and the Java thread scheduler uses these priorities to determine the order in which threads are executed.
There are two common ways to create threads in Java:
1. By extending the Thread
class.
2. By implementing the Runnable
interface.
Creating a Thread by Extending the Thread
Class
// Method 1: Creating thread by extending Thread class
class CustomThread extends Thread {
// The run() method defines the thread's task
public void run() {
System.out.println("Thread running by extending Thread class");
}
public static void main(String[] args) {
// Creating an instance of CustomThread
CustomThread thread = new CustomThread();
// Starting the thread
thread.start();
}
}
Output:
Thread running by extending Thread class
Creating a Thread by Implementing the Runnable
Interface
// Method 2: Creating thread by implementing Runnable interface
class MyRunnable implements Runnable {
// Defining the task for the thread in run() method
public void run() {
System.out.println("Thread running by implementing Runnable interface");
}
public static void main(String[] args) {
// Creating an instance of MyRunnable
MyRunnable runnable = new MyRunnable();
// Passing the Runnable object to a Thread
Thread thread = new Thread(runnable);
// Starting the thread
thread.start();
}
}
Output:
Thread running by implementing Runnable interface
Thread Class in Java
In Java, the Thread
class provides various methods to manage and control threads. Every thread starts its execution with the start()
method, which looks for the run()
method to execute.
Constructors of the Thread
class:
Constructor | Description |
---|---|
Thread() | Creates a new thread. |
Thread(Runnable target) | Creates a new thread with a target Runnable object. |
Thread(String name) | Creates a new thread with a specified name. |
Thread(ThreadGroup group, String name) | Creates a thread within a specified group with a name. |
Common Methods of the Thread Class
Method | Description |
---|---|
activeCount() | Returns the number of active threads in the current thread’s group. |
currentThread() | Returns a reference to the currently executing thread. |
getName() | Returns the name of the thread. |
setName(String name) | Sets the name of the thread. |
getPriority() | Returns the priority of the thread. |
setPriority(int priority) | Sets the priority of the thread. |
isAlive() | Checks if the thread is still running. |
interrupt() | Interrupts the thread. |
join() | Waits for the thread to finish. |
sleep(long millis) | Puts the thread to sleep for the specified time. |
yield() | Causes the thread to temporarily pause to allow other threads to execute. |
Example:
// Java program demonstrating various methods of Thread class
class Task implements Runnable {
public void run() {
try {
System.out.println("Task thread sleeping for 2 seconds.");
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Task thread interrupted.");
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
// Creating instances of Runnable and Thread
Task task = new Task();
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
// Start the threads
thread1.start();
thread2.start();
// Get thread details
System.out.println("Thread 1 Name: " + thread1.getName());
System.out.println("Thread 1 ID: " + thread1.getId());
// Setting new name for thread
thread1.setName("Worker-1");
System.out.println("New name for thread 1: " + thread1.getName());
// Check thread priority
System.out.println("Thread 1 Priority: " + thread1.getPriority());
// Check if thread is alive
System.out.println("Is thread1 alive? " + thread1.isAlive());
// Make thread1 a daemon thread
thread1.setDaemon(true);
System.out.println("Is thread1 a daemon thread? " + thread1.isDaemon());
// Interrupt thread2
thread2.interrupt();
System.out.println("Is thread2 interrupted? " + thread2.isInterrupted());
try {
// Wait for thread2 to finish
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Print active thread count
System.out.println("Active thread count: " + Thread.activeCount());
}
}
Output:
Task thread sleeping for 2 seconds.
Task thread sleeping for 2 seconds.
Thread 1 Name: Thread-0
Thread 1 ID: 10
New name for thread 1: Worker-1
Thread 1 Priority: 5
Is thread1 alive? true
Is thread1 a daemon thread? true
Is thread2 interrupted? true
Active thread count: 2
Runnable interface
The java.lang.Runnable
interface is designed for classes that intend to be executed by a thread. In Java, there are two primary ways to create a new thread: by extending the Thread
class or by implementing the Runnable
interface. Implementing Runnable
is preferred when you only need to override the run()
method, and you don’t need to subclass Thread
.
Steps to Create a Thread Using Runnable
1. Create a class that implements the Runnable interface and override the run() method.
2. Create a Thread object and pass the Runnable implementation to the Thread constructor.
3. Call the start() method on the Thread object, which internally invokes the run() method. This method creates a new thread that executes the code inside run(). Calling run() directly will not start a new thread; it will run in the current thread.
Example 1:
public class RunnableExample {
public static void main(String[] args) {
System.out.println("Executing in thread: "
+ Thread.currentThread().getName());
// Creating a thread with a Runnable implementation
Thread thread1 = new Thread(new Task());
thread1.start();
}
// Runnable implementation
static class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is executing the run() method.");
}
}
}
Output:
Executing in thread: main
Thread-0 is executing the run() method.
In this example, two threads are active: the “main” thread, which executes the main()
method, and “Thread-0”, which is created when start()
is invoked. By passing the Task
class (which implements Runnable
) to the Thread
constructor, we create a new thread that executes the run()
method in Task
.
Handling Exceptions in Runnable
When using the Runnable
interface, you can’t throw checked exceptions from the run()
method directly. However, unchecked exceptions (like RuntimeException
) can be thrown. If an uncaught exception occurs, it will be handled by the thread’s exception handler, or the JVM will print a stack trace and terminate the thread.
Example 2:
public class RunnableWithException {
public static void main(String[] args) {
System.out.println("Current thread: "
+ Thread.currentThread().getName());
// Create a thread that handles exceptions inside Runnable
Thread thread2 = new Thread(new ExceptionTask());
thread2.start();
}
// Runnable implementation that handles exceptions
static class ExceptionTask implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running the task.");
try {
throw new IllegalArgumentException("Simulating an exception");
} catch (IllegalArgumentException e) {
System.out.println("Caught IllegalArgumentException!");
e.printStackTrace();
}
// Simulate unchecked exception
int result = 10 / 0; // This will throw ArithmeticException
}
}
}
Output:
Current thread: main
Thread-0 is running the task.
Caught IllegalArgumentException!
java.lang.IllegalArgumentException: Simulating an exception
at RunnableWithException$ExceptionTask.run(RunnableWithException.java:19)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at RunnableWithException$ExceptionTask.run(RunnableWithException.java:25)
at java.lang.Thread.run(Thread.java:748)
Naming a thread and fetching name of current thread
A thread can be thought of as a lightweight process. Threads require fewer resources than processes and share the resources of their parent process. In Java, the main thread is the thread that starts when the program begins execution. Now, let’s explore how we can name threads in different ways.
Methods for Naming Threads
There are two primary methods for naming threads in Java:
1. Direct Method: Passing the thread name at the time of thread creation.
2. Indirect Method: Using the setName()
method to set or change the name of an existing thread.
Directly Passing the Thread Name : This is the simplest way to set a thread’s name in Java. Each thread gets a default name such as Thread-0
, Thread-1
, and so on. However, we can explicitly provide a name for a thread when creating it, as shown in the following example:
// Java Program demonstrating how to set the name
// of a thread at the time of creation
class CustomThread extends Thread {
// Constructor that takes thread name as an argument
CustomThread(String name) {
super(name); // Call parent class constructor
}
// Overriding the run() method
@Override
public void run() {
System.out.println("Thread is running...");
}
}
public class ThreadNamingDemo {
public static void main(String[] args) {
// Creating two threads with custom names
CustomThread thread1 = new CustomThread("Worker1");
CustomThread thread2 = new CustomThread("Worker2");
// Printing the names of the created threads
System.out.println("Thread 1 name: " + thread1.getName());
System.out.println("Thread 2 name: " + thread2.getName());
// Starting both threads
thread1.start();
thread2.start();
}
}
Output:
x + y = 19
x - y = 11
x * y = 60
x / y = 3
x % y = 3
Output:
Thread 1 name: Worker1
Thread 2 name: Worker2
Thread is running...
Thread is running...
n this example, we created two threads and passed their names at the time of instantiation. We used the getName()
method to retrieve the thread names.
Using the setName()
Method : Another way to name a thread is by using the setName()
method after the thread has been created. This method allows us to rename the thread at any point before or after the thread starts running.
// Java Program demonstrating how to change the
// name of a thread using the setName() method
class TaskThread extends Thread {
@Override
public void run() {
System.out.println("Thread is executing...");
}
}
public class ThreadNamingDemo {
public static void main(String[] args) {
// Creating two threads
TaskThread thread1 = new TaskThread();
TaskThread thread2 = new TaskThread();
// Fetching default thread names
System.out.println("Initial Thread 1 name: " + thread1.getName());
System.out.println("Initial Thread 2 name: " + thread2.getName());
// Starting both threads
thread1.start();
thread2.start();
// Changing the names of the threads
thread1.setName("TaskRunner1");
thread2.setName("TaskRunner2");
// Printing the new thread names
System.out.println("Thread names after renaming:");
System.out.println("Thread 1 new name: " + thread1.getName());
System.out.println("Thread 2 new name: " + thread2.getName());
}
}
Output:
Initial Thread 1 name: Thread-0
Initial Thread 2 name: Thread-1
Thread is executing...
Thread is executing...
Thread names after renaming:
Thread 1 new name: TaskRunner1
Thread 2 new name: TaskRunner2
In this example, we first fetch and print the default thread names, then change the names using setName()
and print the new names.
Fetching the Name of the Current Thread
To get the name of the currently executing thread, we can use the currentThread()
method, which is part of the Thread
class. This method returns a reference to the currently running thread.
// Java Program demonstrating how to get the name
// of the current executing thread
class ActiveThread extends Thread {
@Override
public void run() {
System.out.println("Fetching the current thread name...");
System.out.println(Thread.currentThread().getName());
}
}
public class ThreadNamingDemo {
public static void main(String[] args) {
// Creating and starting two threads
ActiveThread thread1 = new ActiveThread();
ActiveThread thread2 = new ActiveThread();
thread1.start();
thread2.start();
}
}
Output:
Fetching the current thread name...
Thread-0
Fetching the current thread name...
Thread-1
What does start() function do in multithreading in Java?
In Java, threads are typically created in one of two ways:
1. Extending the Thread class
2. Implementing the Runnable interface
In both approaches, we override the run()
method to define the task that the thread will execute. However, instead of directly calling the run()
method, we call the start()
method to begin the execution of the thread. This raises an important question: why not directly call the run()
method, and what makes start()
essential for thread execution?
Why Call start()
Instead of run()
?
When we invoke the start()
method, it performs the necessary setup for the thread. Specifically, it creates a new call stack for the thread and then calls the run()
method on that new stack. This ensures that the thread runs independently of the main thread. On the other hand, if you directly call the run()
method, it simply runs like any other method, using the same call stack as the main thread, and doesn’t create a new thread of execution.
What Happens When a Function is Called?
When a function is invoked, several operations take place:
1. The function’s arguments are evaluated.
2. A new stack frame is pushed onto the call stack.
3. Parameters are initialized with the arguments passed to the function.
4. The body of the function is executed.
5. Once the function finishes execution, the result (if any) is returned, and the current stack frame is popped from the call stack.
The role of the start()
method is to create a separate call stack for the new thread, after which the run()
method is automatically invoked by the JVM in the context of this new thread.
What Happens if We Call run()
Directly?
Let’s consider what happens if we skip the start()
method and directly call the run()
method. The following example demonstrates this:
// Java program to illustrate the difference between
// calling run() and start()
class CustomThread extends Thread {
@Override
public void run() {
try {
// Printing the thread that is executing
System.out.println("Thread " +
Thread.currentThread().getId() +
" is executing");
} catch (Exception e) {
// Handling any exception
System.out.println("Exception caught");
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
// Number of threads to create
int numThreads = 5;
// Creating multiple threads using a loop
for (int i = 0; i < numThreads; i++) {
CustomThread thread = new CustomThread();
// Directly calling run() instead of start()
thread.run();
}
}
}
Output:
Thread 1 is executing
Thread 1 is executing
Thread 1 is executing
Thread 1 is executing
Thread 1 is executing
What does start() function do in multithreading in Java?
The Thread
class in Java is used to represent a thread of execution in a program. It is part of the java.lang
package and contains the sleep()
method, which allows the current thread to pause its execution for a specified period. There are two overloaded versions of the sleep()
method in the Thread
class: one with a single parameter and another with two parameters.
The sleep()
method is used to temporarily suspend the execution of the current thread. Once the specified sleep duration has passed, the thread resumes execution.
Key Points Regarding the Thread.sleep()
Method:
- Whenever the
Thread.sleep()
method is called, it pauses the execution of the current thread. - If another thread interrupts a sleeping thread, an
InterruptedException
is thrown. - Depending on the system’s load, the actual sleep time might be longer than the specified time.
- If the system load is low, the actual sleep time will be close to the specified time.
Syntax of the Thread.sleep()
Method:
There are 4 variations of the sleep()
method in the Thread
class:
1. public static void sleep(long millis) throws InterruptedException
2. public static void sleep(long millis) throws IllegalArgumentException
3. public static void sleep(long millis, int nanos) throws InterruptedException
4. public static void sleep(long millis, int nanos) throws IllegalArgumentException
Parameters of the sleep()
Method:
- millis: The duration in milliseconds for which the thread should pause its execution.
- nanos: The additional time in nanoseconds (optional), ranging from 0 to 999999.
Return Type:
The sleep()
method does not return a value. It has a void
return type: The single-parameter version of the sleep()
method is a native method, meaning its implementation is done in a language other than Java, while the two-parameter version is implemented in Java. Both methods are static
and throw a checked exception, meaning the exception must be handled using either the throws
keyword or a try-catch block.
The Thread.sleep()
method can be used with the main thread or any other thread created programmatically.
1. Using Thread.sleep()
with the Main Thread
This example demonstrates how the Thread.sleep()
method can be used to pause the execution of the main thread:
// Java program to pause the main thread
class MainThreadSleepExample {
public static void main(String[] args) {
try {
for (int i = 1; i <= 4; i++) {
// Pause the main thread for 2 seconds each iteration
Thread.sleep(2000);
// Print the current iteration count
System.out.println("Iteration: " + i);
}
} catch (InterruptedException e) {
// Catching any interruption exception
System.out.println("Main thread interrupted: " + e);
}
}
}
Output:
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
2. Using Thread.sleep()
with a Custom Thread
In this example, we create a custom thread by extending the Thread
class and using the Thread.sleep()
method to pause the execution of that custom thread.
// Java program to pause a custom thread
class CustomThreadSleepExample extends Thread {
@Override
public void run() {
try {
for (int i = 1; i <= 3; i++) {
// Pause the custom thread for 1 second
Thread.sleep(1000);
// Print the current iteration count from the custom thread
System.out.println("Custom Thread Iteration: " + i);
}
} catch (InterruptedException e) {
// Catching any interruption exception
System.out.println("Custom thread interrupted: " + e);
}
}
public static void main(String[] args) {
CustomThreadSleepExample thread = new CustomThreadSleepExample();
thread.start();
}
}
Output:
Custom Thread Iteration: 1
Custom Thread Iteration: 2
Custom Thread Iteration: 3
3. IllegalArgumentException
When Sleep Time is Negative
This example shows what happens when an invalid (negative) value is passed to the Thread.sleep()
method:
// Java program to demonstrate IllegalArgumentException in Thread.sleep()
class NegativeSleepTimeExample {
public static void main(String[] args) {
try {
for (int i = 0; i < 3; i++) {
// Attempt to sleep the thread with a negative time value
Thread.sleep(-500);
// Print the current iteration count (won't be reached due to the exception)
System.out.println("Iteration: " + i);
}
} catch (IllegalArgumentException e) {
// Catching IllegalArgumentException for negative timeout value
System.out.println("Caught IllegalArgumentException: Timeout value is negative");
} catch (InterruptedException e) {
// Catching any interruption exception
System.out.println("Thread interrupted: " + e);
}
}
}
Output:
Caught IllegalArgumentException: Timeout value is negative