Threads in Java allow concurrent execution of two or more parts of a program, enabling multitasking and efficient resource usage.
1. What is a Thread in Java?
- Definition: A thread is a lightweight sub-process, the smallest unit of processing.
- Purpose: Enables a program to perform multiple tasks simultaneously.
- Multithreading: A Java feature where multiple threads execute concurrently.
2. Lifecycle of a Thread
A thread in Java goes through the following states:
- New (Created):
- A thread is created using the
Threadclass or implementingRunnable. - It remains in the new state until the
start()method is called.
Thread t = new Thread(); - A thread is created using the
- Runnable:
- After calling
start(), the thread enters the runnable state, waiting for the CPU to schedule its execution.
t.start(); - After calling
- Running:
- When the thread scheduler picks the thread, it enters the running state.
- Blocked/Waiting:
- The thread is waiting for resources or other threads to complete.
- Terminated (Dead):
- The thread finishes execution or is explicitly stopped.
System.out.println("Thread finished.");
3. Creating Threads in Java
1. Extending the Thread Class
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running.");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
2. Implementing the Runnable Interface
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running.");
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
3. Using Lambda Expressions
public class LambdaThreadExample {
public static void main(String[] args) {
Thread t = new Thread(() -> System.out.println("Thread is running."));
t.start();
}
}
4. Thread Class Methods
| Method | Description |
|---|---|
start() | Starts the thread and invokes the run() method. |
run() | Contains the code that constitutes the thread’s task. |
sleep(long millis) | Causes the thread to pause execution for the specified time in milliseconds. |
join() | Waits for the thread to finish execution. |
getName() | Returns the thread’s name. |
setName(String name) | Sets the thread’s name. |
getPriority() | Returns the thread’s priority. |
setPriority(int p) | Sets the thread’s priority (values between 1 and 10). |
isAlive() | Checks if the thread is alive. |
yield() | Hints the scheduler to allow other threads to execute. |
interrupt() | Interrupts the thread. |
5. Thread Priority
- Default Priority:
- The default priority of a thread is
5(NORM_PRIORITY).
- The default priority of a thread is
- Priority Levels:
- MIN_PRIORITY:
1 - NORM_PRIORITY:
5 - MAX_PRIORITY:
10
- MIN_PRIORITY:
- High Priority: Threads with higher priority are given more CPU time by the thread scheduler.
- Low Priority: Threads with lower priority are given less CPU time.
Example:
public class ThreadPriorityExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> System.out.println("Thread 1 running"));
Thread t2 = new Thread(() -> System.out.println("Thread 2 running"));
t1.setPriority(Thread.MIN_PRIORITY); // Priority 1
t2.setPriority(Thread.MAX_PRIORITY); // Priority 10
t1.start();
t2.start();
}
}
6. Thread Synchronization
Synchronization is used to control access to shared resources to prevent thread interference.
Example: Synchronized Method
class Table {
synchronized void printTable(int n) {
for (int i = 1; i <= 5; i++) {
System.out.println(n * i);
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread extends Thread {
Table table;
MyThread(Table table) {
this.table = table;
}
public void run() {
table.printTable(5);
}
}
public class SynchronizationExample {
public static void main(String[] args) {
Table table = new Table();
MyThread t1 = new MyThread(table);
MyThread t2 = new MyThread(table);
t1.start();
t2.start();
}
}
7. Inter-thread Communication
Threads communicate using methods like wait(), notify(), and notifyAll().
Example:
class Chat {
boolean flag = false;
public synchronized void question(String msg) {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = true;
notify();
}
public synchronized void answer(String msg) {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = false;
notify();
}
}
public class InterThreadCommunication {
public static void main(String[] args) {
Chat chat = new Chat();
new Thread(() -> {
String[] questions = {"Hi", "How are you?", "Bye"};
for (String question : questions) {
chat.question(question);
}
}).start();
new Thread(() -> {
String[] answers = {"Hello", "I'm fine", "Goodbye"};
for (String answer : answers) {
chat.answer(answer);
}
}).start();
}
}
8. Daemon Threads
- Definition: Threads that provide background services and do not prevent the JVM from exiting.
- Example: Garbage collection threads.
- Setting a Daemon Thread:
Thread t = new Thread(() -> System.out.println("Daemon thread"));
t.setDaemon(true);
t.start();
9. Thread Pooling
- Definition: A pool of threads that can be reused to execute tasks.
- Advantage: Reduces overhead of thread creation and destruction.
Example:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 5; i++) {
int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running." + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
10. Advantages of Multithreading
- Improved Performance: Efficient CPU utilization.
- Resource Sharing: Multiple threads share common resources.
- Concurrency: Multiple tasks run simultaneously.
- Reduced Response Time: Faster execution of independent tasks.
11. Common Issues in Multithreading
- Deadlock:
- Occurs when two or more threads wait indefinitely for each other to release resources.
- Race Condition:
- Multiple threads accessing shared resources simultaneously, leading to inconsistent results.
- Thread Interference:
- Threads modify shared resources unpredictably.
- Starvation:
- A thread is perpetually denied access to resources.
Conclusion
Threads in Java enable concurrent programming, making applications efficient and responsive. Understanding thread lifecycle, synchronization, inter-thread communication, and thread pooling is essential for writing robust multithreaded applications.