In Java, processes and threads are fundamental concepts for enabling multitasking and concurrency. Understanding their differences, roles, and implementation is crucial for designing efficient and responsive applications.
What is a Process?
- Definition: A process is an independent instance of a running program. Each process has its own memory space and system resources, isolated from other processes.
- Characteristics:
- Heavyweight: Processes require significant overhead as they operate in separate memory spaces.
- Communication between processes is complex and often involves inter-process communication (IPC) mechanisms.
- Failure in one process does not affect others.
- Example of Processes in Java: Java programs themselves run as a process, and external processes can be spawned using the
ProcessBuilderorRuntimeclasses.
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
Process process = Runtime.getRuntime().exec("notepad.exe");
System.out.println("Notepad launched");
} catch (IOException e) {
e.printStackTrace();
}
}
}
What is a Thread?
- Definition: A thread is a smaller, lightweight unit of execution within a process. Threads share the same memory space and resources of the parent process.
- Characteristics:
- Lightweight: Threads are more efficient than processes as they share memory and resources.
- Communication between threads is easier since they operate within the same memory space.
- Failure in one thread can potentially impact others in the same process.
Key Differences Between Processes and Threads:
| Feature | Process | Thread |
|---|---|---|
| Memory Space | Separate for each process. | Shared among threads in a process. |
| Communication | Complex and slower (IPC needed). | Easier and faster (shared memory). |
| Overhead | High (independent resources). | Low (shared resources). |
| Failure Isolation | Independent (failure in one doesn’t affect others). | Interdependent (failure may affect others). |
| Control | Managed by the operating system. | Managed by the JVM within the process. |
| Creation Time | Slower (due to resource allocation). | Faster (lighter to create). |
| Dependency | Processes are independent. | Threads depend on the parent process. |
| Memory Control | Each process has its own memory. | Threads share memory and stack space. |
Multithreading in Java:
Java provides built-in support for multithreading to allow multiple tasks to execute concurrently within a single process.
Creating Threads in Java:
- Extending the
ThreadClass:- Example:
class MyThread extends Thread {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
- Implementing the
RunnableInterface:- Example:
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
- Using Lambda Expressions (Java 8+):
- Example:
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("Thread running: " + Thread.currentThread().getName());
});
t1.start();
}
}
Thread Lifecycle in Java:
- New:
- The thread is created but not yet started (e.g.,
Thread t = new Thread();).
- The thread is created but not yet started (e.g.,
- Runnable:
- The thread is ready to run but waiting for CPU time (e.g., after
t.start()).
- The thread is ready to run but waiting for CPU time (e.g., after
- Running:
- The thread is actively executing its task.
- Blocked/Waiting:
- The thread is paused, waiting for a resource or signal (e.g.,
wait()or I/O operation).
- The thread is paused, waiting for a resource or signal (e.g.,
- Terminated:
- The thread has finished execution.
Synchronization in Threads:
When multiple threads access shared resources, synchronization ensures thread safety and avoids race conditions. Refer to the detailed explanation of synchronization methods in the earlier section.
Advantages of Multithreading:
- Improved application responsiveness.
- Efficient CPU utilization.
- Simplifies modeling real-world systems with concurrent tasks.
Challenges of Multithreading:
- Difficult to debug and test.
- Potential for deadlocks and race conditions.
- Overhead in managing thread synchronization.
Functions in Threads
Java provides a variety of methods to control and manage threads. These functions are part of the Thread class and are essential for multi-threaded programming.
Key Thread Functions and Their Usage
start()- Starts the execution of a thread.
- Calls the
run()method of the thread.
Thread t = new Thread(() -> System.out.println("Thread started!")); t.start();run()- Contains the code that constitutes the thread’s task.
- Called internally by the
start()method.
class MyThread extends Thread { public void run() { System.out.println("Running in a thread!"); } } MyThread t = new MyThread(); t.start();join()- Causes the current thread to wait until the specified thread has finished execution.
- a
Thread t1 = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("Thread 1: " + i); } }); t1.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread 1 has finished.");sleep(long millis)- Causes the current thread to pause for the specified duration.
- Useful for simulating delays.
Thread.sleep(1000); // Pauses for 1 secondyield()- Hints the scheduler that the current thread is willing to yield its execution to other threads.
- The thread moves to the ready state but might not be immediately scheduled.
Thread.yield(); System.out.println("Thread yielded!");interrupt()- Interrupts a thread that is in a waiting or sleeping state.
- The interrupted thread throws an
InterruptedException.
Thread t = new Thread(() -> { try { Thread.sleep(5000); } catch (InterruptedException e) { System.out.println("Thread interrupted!"); } }); t.start(); t.interrupt();isAlive()- Returns
trueif the thread is still running or in a runnable state. - Useful for checking thread status.
Thread t = new Thread(() -> System.out.println("Running")); t.start(); System.out.println("Is thread alive? " + t.isAlive());- Returns
getName()** and **setName(String name)- Retrieves or sets the name of the thread.
Thread t = new Thread(); t.setName("MyThread"); System.out.println("Thread name: " + t.getName());getPriority()** and **setPriority(int priority)- Retrieves or sets the priority of the thread (range:
Thread.MIN_PRIORITYtoThread.MAX_PRIORITY).
Thread t = new Thread(); t.setPriority(Thread.MAX_PRIORITY); System.out.println("Thread priority: " + t.getPriority());- Retrieves or sets the priority of the thread (range:
currentThread()- Returns a reference to the currently executing thread.
Thread current = Thread.currentThread(); System.out.println("Current thread: " + current.getName());
Example: Producer-Consumer Using wait and notify
Code:
class SharedResource {
private int value = 0;
private boolean produced = false;
public synchronized void produce() {
while (produced) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
value++;
System.out.println("Produced: " + value);
produced = true;
notify();
}
public synchronized void consume() {
while (!produced) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("Consumed: " + value);
produced = false;
notify();
}
}
public class WaitNotifyExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.produce();
}
});
Thread consumer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.consume();
}
});
producer.start();
consumer.start();
}
}
Output:
Produced: 1 Consumed: 1 Produced: 2 Consumed: 2 Produced: 3 Consumed: 3 Produced: 4 Consumed: 4 Produced: 5 Consumed: 5
Summary
Java provides a rich set of thread functions to manage and coordinate multi-threaded programs. Key functions like start(), join(), wait(), and notify() ensure proper synchronization and execution flow. Understanding and utilizing these functions effectively allows for efficient and safe concurrent programming.