JavaJava Thread Methods: A Comprehensive Guide

Java Thread Methods: A Comprehensive Guide

Java programming tutorial

Java Threads allow multiple tasks to run concurrently within a single program. This programming tutorial explores various methods for managing threads in Java. In particular, we will review methods that deal with thread states and properties, as well as their synchronization and interruption. We also discuss methods for controlling thread priority, daemon threads, sleeping and waiting, as well as a couple of miscellaneous methods that do not fall into any of the aforementioned categories.

Before reading this tutorial, we recommend reviewing our Guide to the Java Thread Lifecycle.

How to Start a Thread in Java

start()

The start() method initiates the execution of a thread. It calls the run() method defined in your thread class or runnable object. Invoking run() directly will not start a new thread, so it is crucial to use start(). Here is a code example showing its use:

public class Main {
    public static void main(String[] args) {
        Thread myThread = new Thread(new MyRunnable());
        myThread.start();
    }
}
      

run()

The run() method contains the code that will be executed in the thread. It must be overridden when extending the Thread class or implementing the Runnable interface.


class MyRunnable implements Runnable {
    public void run() {
        System.out.println("This is a runnable.");
    }
}
        

Thread States and Properties

getState()

The getState() method returns the current state of the thread as an integer. The possible states are:

  • NEW: The thread has been created but has not started yet.
  • RUNNABLE: The thread is actively executing or is ready to execute.
  • BLOCKED: The thread is blocked, waiting for a monitor lock.
  • WAITING: The thread is waiting indefinitely for another thread to perform a specific action.
  • TIMED_WAITING: The thread is waiting for a specified time period.
  • TERMINATED: The thread has completed its execution and terminated.

Here is some example code illustrating the above thread states:

Thread myThread = new Thread(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

System.out.println(myThread.getState()); // Output: NEW

myThread.start();
System.out.println(myThread.getState()); // Output: RUNNABLE

try {
    myThread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println(myThread.getState()); // Output: TERMINATED

In this example, we created a thread (myThread), started it, and then checked its state at different points in its lifecycle.

isAlive()

The isAlive() method checks whether the thread is alive. A thread is considered alive if it has been started and has not yet completed. It returns true if the thread is alive, and false otherwise.

In the following code example, we create a new thread (myThread) but have not started it yet. We then check if the thread is alive using the isAlive() method, which will return false. After starting the thread, we check again, and this time it will return true:

Thread myThread = new Thread();
System.out.println(myThread.isAlive()); // Output: false
myThread.start();
System.out.println(myThread.isAlive()); // Output: true

getName()

The getName() method returns the name of the thread.

In this Java code example, we create a new thread (myThread) without specifying a name. The getName() method is used to retrieve and print the default name of the thread, which will be something like Thread-0:

Thread myThread = new Thread();
System.out.println(myThread.getName()); // Output: Thread-0

setName()

The setName() method sets the name of the thread.

In the next example code, we create a new thread (myThread) and then use setName() to set a custom name for the thread. We then use getName() again to retrieve and print the custom name:

Thread myThread = new Thread();
myThread.setName("CustomThread");
System.out.println(myThread.getName()); // Output: CustomThread

getId()

The getID() method returns the unique identifier for the thread.

In this example, we create a new thread (myThread) and then use getId() to retrieve and print the unique identifier assigned to the thread. The value will be platform-dependent:

Thread myThread = new Thread();
System.out.println(myThread.getId()); // Output: A unique identifier (platform-dependent)

getState()

The getState() method returns the current state of the thread as an enum value, which provides a more human-readable representation compared to the integer values returned by getState().

In the code example below, we create a new thread (myThread) and then use getState() to retrieve and print the current state of the thread. We also demonstrate how to get the name of the state using name(), which will be NEW in this case:

Thread myThread = new Thread();
System.out.println(myThread.getState()); // Output: NEW
System.out.println(myThread.getState().name()); // Output: NEW

Read: Best Collaboration Tools for Java Developers

Thread Synchronization and Interruption in Java

join()

The join() method allows one thread to wait for the completion of another. This can be useful when you need to ensure certain tasks are finished before moving on.

In the following code example, we have two threads (thread1 and thread2) that count from 1 to 5 with a delay of 1 second between each count. The main thread starts both of these threads:

public class JoinExample {

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Thread 1: Count " + i);
                try {
                    Thread.sleep(1000); // Simulating some work
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Thread 2: Count " + i);
                try {
                    Thread.sleep(1000); // Simulating some work
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

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

        try {
            thread1.join(); // Main thread waits for thread1 to finish
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Thread 1 has completed.");
    }
}

interrupt()

Java’s interrupt() method interrupts the thread, causing it to throw an InterruptedException the next time it checks for interrupts.

In this example, we create a thread (myThread) that counts from 1 to 5 with a delay of 1 second between each count. Inside the thread, we have a try-catch block to handle InterruptedException:

public class InterruptExample {

    public static void main(String[] args) {
        Thread myThread = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("Count: " + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted.");
            }
        });

        myThread.start();

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

isInterrupted()

The isInterrupted() method Checks whether the thread has been interrupted. Unlike interrupt(), this does not clear the interrupted flag.

In our next code example, we create a thread (myThread) that counts from 1 to 5 with a delay of 1 second between each count. Inside the thread, we have a try-catch block to handle InterruptedException:

public class IsInterruptedExample {

    public static void main(String[] args) {
        Thread myThread = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("Count: " + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted.");
            }
        });

        myThread.start();

        try {
            Thread.sleep(3000); // Main thread sleeps for 3 seconds
            myThread.interrupt(); // This will interrupt myThread
            boolean interruptedStatus = myThread.isInterrupted(); // Check if interrupted
            System.out.println("Thread interrupted status: " + interruptedStatus);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

There is also a static version of isInterrupted(). The Thread.interrupted() method checks whether the current thread has been interrupted and clears the interrupted flag.

Read: IntelliJ IDEA Review

Methods for Controlling Thread Priority in Java

getPriority()

The join() method allows one thread to wait for the completion of another. This can be useful when you need to ensure certain tasks are finished before moving on.

setPriority(int priority)

The sleep() method pauses the execution of a thread for a specified amount of time, in milliseconds. This can be useful for introducing delays in your program.

Here is a short program that demonstrates the use of both methods:

public class PriorityExample {

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Thread 1: Count " + i);
            }
        });

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

        // Get and display the default priorities
        System.out.println("Default Priority of Thread 1: " + thread1.getPriority());
        System.out.println("Default Priority of Thread 2: " + thread2.getPriority());

        // Set priorities
        thread1.setPriority(Thread.MIN_PRIORITY); // Set priority to minimum
        thread2.setPriority(Thread.MAX_PRIORITY); // Set priority to maximum

        // Display updated priorities
        System.out.println("Updated Priority of Thread 1: " + thread1.getPriority());
        System.out.println("Updated Priority of Thread 2: " + thread2.getPriority());

        // Start the threads
        thread1.start();
        thread2.start();
    }
}

In this example:

  1. We create two threads, thread1 and thread2.
  2. We use getPriority() to retrieve and display the default priorities of both threads.
  3. We use setPriority() to set the priority of thread1 to the minimum priority and the priority of thread2 to the maximum priority.
  4. We use getPriority() again to display the updated priorities.
  5. Finally, we start both threads.

When you run this program, you will see the output indicating the default and updated priorities of the threads. Keep in mind that the actual behavior may vary depending on the underlying operating system and JVM implementation.

Daemon Threads in Java

setDaemon(boolean on)

The setDaemon(boolean on) method marks the thread as either a daemon thread or a user thread. Daemon threads are automatically terminated when all user threads have completed.

isDaemon()

The isDaemon() method checks whether the thread is a daemon thread.

Here is a short code snippet demonstrating the use of both methods:

public class DaemonExample {

    public static void main(String[] args) {
        Thread userThread = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("User Thread: Count " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread daemonThread = new Thread(() -> {
            while (true) {
                System.out.println("Daemon Thread: Running...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // Set daemon status
        daemonThread.setDaemon(true);

        // Check daemon status
        System.out.println("Is daemonThread a daemon thread? " + daemonThread.isDaemon());

        userThread.start();
        daemonThread.start();
    }
}

In this example code:

  1. We create two threads, userThread and daemonThread: userThread is a regular user thread that counts from 1 to 5 with a delay of 1 second between each count, while daemonThread is a daemon thread that runs in an infinite loop, printing a message every second.
  2. We use setDaemon(true) to mark daemonThread as a daemon thread.
  3. We use isDaemon() to check whether daemonThread is a daemon thread.

When you run this program, you will see the output indicating the status of daemonThread and you will observe that even if userThread completes, daemonThread continues to run in the background until the program is terminated.

Sleeping and Waiting

sleep(long millis[, long nanos])

The sleep(long millis[, long nanos]) method causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds. The optional second argument provides finer control of sleep duration with nanoseconds.

wait()

The wait() method causes the current thread to wait until another thread calls notify() or notifyAll() on the same object.

notify()

The notify() wakes up a single thread that is waiting on this object’s monitor.

notifyAll()

The notifyAll() wakes up all threads that are waiting on this object’s monitor.

Here is some example Java code demonstrating the use of the sleeping and waiting methods described above:

public class SynchronizationExample {

    public static void main(String[] args) {
        Object lock = new Object(); // Object used for synchronization

        Thread producer = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Producer: Producing data...");
                try {
                    Thread.sleep(2000); // Simulating some work
                    lock.notify(); // Notify waiting thread
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Consumer: Waiting for data...");
                try {
                    lock.wait(); // Wait for notification from producer
                    System.out.println("Consumer: Data received.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        producer.start();
        consumer.start();
    }
}

In this example:

  1. We create an Object called lock which will be used for synchronization.
  2. producer is a thread that simulates producing some data. It holds the lock on lock, produces data, and then notifies the waiting thread.
  3. The consumer thread waits for data. It also holds the lock on lock and waits for notification from the producer.
  4. producer and consumer are started.

When you run this program, you will see the output indicating the sequence of actions performed by the producer and consumer threads.

Miscellaneous Java Thread Methods

currentThread()

currentThread() Returns a reference to the currently executing thread object.

yield()

The yield() method causes the currently executing thread to pause and allow other threads to execute.

Here is an example showing how to use the yield() method in Java:

public class CurrentThreadAndYieldExample {

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running.");
            Thread.yield(); // Suggest to the scheduler that it can give a turn to another thread
            System.out.println("Thread 1 is running again.");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running.");
        });

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

        Thread mainThread = Thread.currentThread();
        System.out.println("Main thread name: " + mainThread.getName());
    }
}

In the above code:

  1. We create two threads, thread1 and thread2.
  2. thread1 prints a message, then calls Thread.yield(), suggesting to the scheduler that it can give a turn to another thread. After that, thread1 prints another message.
  3. thread2 simply prints a message.
  4. Both thread1 and thread2 are started.
  5. We use Thread.currentThread() to get a reference to the main thread and print its name.

When you run this program, you will see output indicating the sequence of actions performed by the threads.

Final Thoughts on Java Thread Methods

This programming tutorial explored Java Thread methods that deal with thread states and properties, as well as their synchronization and interruption. We also covered Java Thread methods for controlling thread priority, daemon threads, sleeping, and waiting, along with some miscellaneous methods.

Remember to handle exceptions appropriately when working with threads, as they can introduce unique challenges in concurrent programming.

Read: Online Courses to Learn Java Software Development

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories