Posted on: January 18, 2025 Posted by: rahulgite Comments: 0

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 ProcessBuilder or Runtime classes.
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:

FeatureProcessThread
Memory SpaceSeparate for each process.Shared among threads in a process.
CommunicationComplex and slower (IPC needed).Easier and faster (shared memory).
OverheadHigh (independent resources).Low (shared resources).
Failure IsolationIndependent (failure in one doesn’t affect others).Interdependent (failure may affect others).
ControlManaged by the operating system.Managed by the JVM within the process.
Creation TimeSlower (due to resource allocation).Faster (lighter to create).
DependencyProcesses are independent.Threads depend on the parent process.
Memory ControlEach 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:

  1. Extending the Thread Class:
    • 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();
    }
}
  1. Implementing the Runnable Interface:
    • 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();
    }
}
  1. 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:

  1. New:
    • The thread is created but not yet started (e.g., Thread t = new Thread();).
  2. Runnable:
    • The thread is ready to run but waiting for CPU time (e.g., after t.start()).
  3. Running:
    • The thread is actively executing its task.
  4. Blocked/Waiting:
    • The thread is paused, waiting for a resource or signal (e.g., wait() or I/O operation).
  5. 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

  1. start()
    • Starts the execution of a thread.
    • Calls the run() method of the thread.
    Example: Thread t = new Thread(() -> System.out.println("Thread started!")); t.start();
  2. run()
    • Contains the code that constitutes the thread’s task.
    • Called internally by the start() method.
    Example: class MyThread extends Thread { public void run() { System.out.println("Running in a thread!"); } } MyThread t = new MyThread(); t.start();
  3. join()
    • Causes the current thread to wait until the specified thread has finished execution.
    • a
    xample: 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.");
  4. sleep(long millis)
    • Causes the current thread to pause for the specified duration.
    • Useful for simulating delays.
    Example: Thread.sleep(1000); // Pauses for 1 second
  5. yield()
    • 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.
    Example: Thread.yield(); System.out.println("Thread yielded!");
  6. interrupt()
    • Interrupts a thread that is in a waiting or sleeping state.
    • The interrupted thread throws an InterruptedException.
    Example: Thread t = new Thread(() -> { try { Thread.sleep(5000); } catch (InterruptedException e) { System.out.println("Thread interrupted!"); } }); t.start(); t.interrupt();
  7. isAlive()
    • Returns true if the thread is still running or in a runnable state.
    • Useful for checking thread status.
    Example: Thread t = new Thread(() -> System.out.println("Running")); t.start(); System.out.println("Is thread alive? " + t.isAlive());
  8. getName()** and **setName(String name)
    • Retrieves or sets the name of the thread.
    Example: Thread t = new Thread(); t.setName("MyThread"); System.out.println("Thread name: " + t.getName());
  9. getPriority()** and **setPriority(int priority)
    • Retrieves or sets the priority of the thread (range: Thread.MIN_PRIORITY to Thread.MAX_PRIORITY).
    Example: Thread t = new Thread(); t.setPriority(Thread.MAX_PRIORITY); System.out.println("Thread priority: " + t.getPriority());
  10. currentThread()
    • Returns a reference to the currently executing thread.
    Example: 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.

Leave a Comment