Thread Class in Java

Introduction

The Thread class in Java is the primary mechanism for creating and managing threads. Each thread represents a separate path of execution within a program, enabling concurrent operations and improving application performance and responsiveness.

Table of Contents

  1. Overview of Thread Class
  2. Creating Threads
    • Extending the Thread Class
    • Implementing the Runnable Interface
  3. Thread Lifecycle
  4. Thread Methods
  5. Example: Extending Thread Class
  6. Example: Implementing Runnable Interface
  7. Thread Priorities
  8. Thread Synchronization
  9. Inter-Thread Communication
  10. Thread States
  11. Daemon Threads
  12. Conclusion

1. Overview of Thread Class

The Thread class in Java is part of the java.lang package and provides constructors and methods to create and manage threads. Threads allow a program to operate more efficiently by performing multiple tasks simultaneously.

Key Methods in Thread Class:

  • start(): Starts the execution of the thread.
  • run(): Contains the code that constitutes the new thread.
  • sleep(long millis): Puts the thread to sleep for a specified time.
  • join(): Waits for the thread to die.
  • interrupt(): Interrupts the thread.
  • isAlive(): Tests if the thread is alive.
  • getName(): Returns the name of the thread.
  • setName(String name): Changes the name of the thread.
  • getPriority(): Returns the priority of the thread.
  • setPriority(int priority): Changes the priority of the thread.

2. Creating Threads

Extending the Thread Class

One way to create a thread is by extending the Thread class and overriding its run method.

Example:

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " is running. Count: " + i);
            try {
                Thread.sleep(1000);  // Simulate some work with sleep
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Implementing the Runnable Interface

Another way to create a thread is by implementing the Runnable interface. This approach is preferred when the class is already extending another class.

Example:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " is running. Count: " + i);
            try {
                Thread.sleep(1000);  // Simulate some work with sleep
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3. Thread Lifecycle

A thread in Java goes through various states in its lifecycle:

  1. New: A thread is created but not yet started.
  2. Runnable: A thread is ready to run and waiting for CPU time.
  3. Running: A thread is executing.
  4. Blocked: A thread is blocked waiting for a monitor lock.
  5. Waiting: A thread is waiting indefinitely for another thread to perform a particular action.
  6. Timed Waiting: A thread is waiting for another thread to perform a particular action within a stipulated amount of time.
  7. Terminated: A thread has completed its execution.

4. Thread Methods

start()

Starts the execution of the thread. The JVM calls the run() method of this thread.

thread.start();

run()

If a thread was constructed using a separate Runnable object, then that Runnable object's run method is called; otherwise, this method does nothing and returns.

public void run() {
    // code to be executed
}

sleep()

Puts the current thread to sleep for a specified amount of time.

Thread.sleep(1000); // Sleeps for 1 second

join()

Waits for the thread to die.

thread.join();

interrupt()

Interrupts the thread.

thread.interrupt();

isAlive()

Tests if the thread is alive.

thread.isAlive();

getName() and setName()

Gets or sets the name of the thread.

String name = thread.getName();
thread.setName("New Thread Name");

getPriority() and setPriority()

Gets or sets the priority of the thread.

int priority = thread.getPriority();
thread.setPriority(Thread.MAX_PRIORITY);

5. Example: Extending Thread Class

Example:

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " is running. Count: " + i);
            try {
                Thread.sleep(1000);  // Simulate some work with sleep
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

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

Output:

Thread-0 is running. Count: 0
Thread-1 is running. Count: 0
Thread-0 is running. Count: 1
Thread-1 is running. Count: 1
Thread-0 is running. Count: 2
Thread-1 is running. Count: 2
Thread-0 is running. Count: 3
Thread-1 is running. Count: 3
Thread-0 is running. Count: 4
Thread-1 is running. Count: 4

6. Example: Implementing Runnable Interface

Example:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " is running. Count: " + i);
            try {
                Thread.sleep(1000);  // Simulate some work with sleep
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

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

Output:

Thread-0 is running. Count: 0
Thread-1 is running. Count: 0
Thread-0 is running. Count: 1
Thread-1 is running. Count: 1
Thread-0 is running. Count: 2
Thread-1 is running. Count: 2
Thread-0 is running. Count: 3
Thread-1 is running. Count: 3
Thread-0 is running. Count: 4
Thread-1 is running. Count: 4

7. Thread Priorities

Thread priorities determine the relative priority of threads. In Java, thread priority is an integer value between 1 (MIN_PRIORITY) and 10 (MAX_PRIORITY), with a default value of 5 (NORM_PRIORITY).

Example:

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " with priority " + Thread.currentThread().getPriority() + " is running.");
    }

    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.setPriority(Thread.MIN_PRIORITY); // Set priority to 1
        thread2.setPriority(Thread.MAX_PRIORITY); // Set priority to 10

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

Output:

Thread-0 with priority 1 is running.
Thread-1 with priority 10 is running.

8. Thread Synchronization

Synchronization is used to control the access of multiple threads to shared resources. It prevents data inconsistency and race conditions.

Example:

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SynchronizedExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

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

        thread1.join();
        thread2.join();

        System.out.println("Final count: " + counter.getCount());
    }
}

Output:

Final count: 2000

9. Inter-Thread Communication

Inter-thread communication in Java is achieved using wait(), notify(), and notifyAll() methods. These methods are used to synchronize the activities of multiple threads.

Example:


java
class SharedResource {
    private int data = 0;
    private boolean available = false;

    public synchronized void produce(int value) throws InterruptedException {
        while (available) {
            wait();
        }
        data = value;
        available = true;
        notify();
    }

    public synchronized int consume() throws InterruptedException {
        while (!available) {
            wait();
        }
        available = false;
        notify();
        return data;
    }
}

public class InterThreadCommunicationExample {
    public static void main(String[] args) {
        SharedResource sharedResource = new SharedResource();

        Thread producer = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    sharedResource.produce(i);
                    System.out.println("Produced: " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread consumer = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                try {
                    int value = sharedResource.consume();
                    System.out.println("Consumed: " + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        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

10. Thread States

A thread in Java can be in one of the following states:

  1. New: A thread that has been created but not yet started.
  2. Runnable: A thread that is ready to run and waiting for CPU time.
  3. Blocked: A thread that is blocked waiting for a monitor lock.
  4. Waiting: A thread that is waiting indefinitely for another thread to perform a particular action.
  5. Timed Waiting: A thread that is waiting for another thread to perform a particular action within a specified waiting time.
  6. Terminated: A thread that has exited.

11. Daemon Threads

Daemon threads are low-priority threads that run in the background to perform tasks such as garbage collection. They do not prevent the JVM from exiting when all user threads finish their execution.

Example:

class MyDaemonThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("Daemon thread is running.");
            try {
                Thread.sleep(1000);  // Simulate some work with sleep
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyDaemonThread daemonThread = new MyDaemonThread();
        daemonThread.setDaemon(true);  // Set the thread as a daemon thread
        daemonThread.start();

        try {
            Thread.sleep(3000);  // Main thread sleeps for 3 seconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Main thread is exiting.");
    }
}

Output:

Daemon thread is running.
Daemon thread is running.
Daemon thread is running.
Main thread is exiting.

Explanation:

  • The MyDaemonThread class extends Thread and overrides the run method.
  • The thread is set as a daemon thread using setDaemon(true).
  • The main thread sleeps for 3 seconds and then exits.
  • The daemon thread runs in the background but does not prevent the JVM from exiting.

12. Conclusion

The Thread class in Java provides a powerful mechanism for creating and managing threads. By extending the Thread class or implementing the Runnable interface, you can define tasks that run concurrently. Understanding thread lifecycle, synchronization, inter-thread communication, and daemon threads is essential for writing efficient and robust multithreaded applications.

Happy coding!

Comments