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:
- We create two threads, thread1 and thread2.
- We use getPriority() to retrieve and display the default priorities of both threads.
- We use setPriority() to set the priority of thread1 to the minimum priority and the priority of thread2 to the maximum priority.
- We use getPriority() again to display the updated priorities.
- 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:
- 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.
- We use setDaemon(true) to mark daemonThread as a daemon thread.
- 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:
- We create an Object called lock which will be used for synchronization.
- producer is a thread that simulates producing some data. It holds the lock on lock, produces data, and then notifies the waiting thread.
- The consumer thread waits for data. It also holds the lock on lock and waits for notification from the producer.
- 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:
- We create two threads, thread1 and thread2.
- 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.
- thread2 simply prints a message.
- Both thread1 and thread2 are started.
- 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.