Multithreading

Multithreading in Java allows you to execute multiple threads (smaller units of a process) concurrently, which can help improve the performance and responsiveness of your Java applications. Java provides built-in support for multithreading through the java.lang.Thread class and the java.lang.Runnable interface. Here are the basic concepts and steps involved in working with multithreading in Java:

Creating Threads:

You can create a thread by extending the Thread class or implementing the Runnable interface. Implementing Runnable is often preferred because it allows you to separate the task (runnable object) from the threading mechanism.

Example of extending Thread:

class MyThread extends Thread {
    public void run() {
        // Code to be executed in this thread
    }
}

Example of implementing Runnable:

class MyRunnable implements Runnable {
    public void run() {
        // Code to be executed in this thread
    }
}

Creating Thread Objects:

To start a thread, you need to create a Thread object and pass an instance of your class that extends Thread or implements Runnable to its constructor.

MyThread myThread = new MyThread();
Thread thread = new Thread(new MyRunnable());

Starting Threads:

To start a thread, call the start() method on the Thread object. This method internally calls the run() method defined in your class.

myThread.start();
thread.start();

Thread Synchronization:

When multiple threads access shared resources, you may encounter race conditions. Java provides synchronization mechanisms like synchronized blocks/methods and the java.util.concurrent package to manage thread synchronization and avoid data corruption.

synchronized void synchronizedMethod() {
    // Synchronized code block
}

Thread States:

Threads can be in various states like NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED. Understanding these states can help in debugging and managing threads effectively.

Thread Sleep

The sleep() method pauses the execution of a thread for a specified amount of time.

javaCopy codeThread thread = new Thread(() -> {
    for (int i = 1; i <= 5; i++) {
        System.out.println("Thread: " + i);
        try {
            Thread.sleep(1000); // Sleep for 1 second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

thread.start();

Thread Joining:

You can use the join() method to wait for a thread to finish its execution before continuing with the current thread.

Thread thread1 = new Thread(() -> {
    for (int i = 1; i <= 5; i++) {
        System.out.println("Thread 1: " + i);
    }
});

Thread thread2 = new Thread(() -> {
    for (int i = 1; i <= 5; i++) {
        System.out.println("Thread 2: " + i);
    }
});

thread1.start();
thread2.start();

try {
    thread1.join(); // Wait for thread1 to finish
    thread2.join(); // Wait for thread2 to finish
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("Main thread is done.");

Thread Priorities:

Threads can have different priorities (1-10), and you can set their priority using the setPriority() method. Higher-priority threads get more CPU time, but it's not always guaranteed, as it depends on the underlying OS.

Daemon Threads:

Threads can be marked as daemon threads, which will terminate when all non-daemon threads finish their execution. You can set a thread as a daemon using setDaemon(true).

thread.setDaemon(true);

This example demonstrates the some built-in methods of the Thread class:

javaCopy codepublic class ThreadMethodsExample {
    public static void main(String[] args) throws InterruptedException {
        // Create a new thread
        Thread thread = new Thread(() -> {
            System.out.println("Thread started.");

            try {
                // Sleep for 2 seconds
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted while sleeping.");
                return; // Exit the thread
            }

            System.out.println("Thread resumed after sleep.");

            // Check if the thread is interrupted
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Thread is interrupted.");
            } else {
                System.out.println("Thread is not interrupted.");
            }
        });

        // Start the thread
        thread.start();

        // Wait for the thread to finish
        thread.join();

        // Check if the thread is alive
        if (thread.isAlive()) {
            System.out.println("Thread is still alive.");
        } else {
            System.out.println("Thread has finished.");
        }

        // Interrupt the thread
        thread.interrupt();

        // Check if the thread is interrupted
        if (thread.isInterrupted()) {
            System.out.println("Thread is interrupted after interruption.");
        } else {
            System.out.println("Thread is not interrupted after interruption.");
        }
    }
}
  • start(): Starts the execution of the thread.

  • join(): Waits for the thread to finish executing.

  • getName(): Returns the name of the thread.

  • getPriority(): Returns the priority of the thread.

  • getState(): Returns the state of the thread.

  • sleep(): Causes the thread to sleep for a specified number of milliseconds.

  • yield(): Causes the thread to voluntarily give up the CPU to other threads.

  • interrupt(): Interrupts the thread.

  • isInterrupted(): Checks if the thread has been interrupted.

  • isAlive(): Checks if the thread is still alive.

These methods can be used to control the behavior of threads in your Java applications.

Multithreading in Java can significantly enhance the performance and responsiveness of your applications, but it also introduces challenges related to synchronization and coordination between threads. Careful design and proper synchronization are essential for writing robust multithreaded code.

Last updated