How to use Threads in Java (create, start, pause, interrupt and join)
- Details
- Written by Nam Ha Minh
- Last Updated on 12 August 2019   |   Print Email
In this Java concurrency tutorial we’re going to guide you how to create a thread and then how to perform basic operations on a thread like start, pause, interrupt and join. You will be able to understand exactly how threads are working in Java at the low level.
1. How to create a thread in Java
There are two ways for creating a thread in Java: by extending the Thread class; and by implementing the Runnable interface. Both are in the java.lang package so you don’t have to use import statement.
Then you put the code that needs to be executed in a separate thread inside the run() method which is overridden from the Thread/Runnable. And invoke the start() method on a Thread object to put the thread into running status (alive).
The following class, ThreadExample1, demonstrates the first way:
public class ThreadExample1 extends Thread { public void run() { System.out.println("My name is: " + getName()); } public static void main(String[] args) { ThreadExample1 t1 = new ThreadExample1(); t1.start(); System.out.println("My name is: " + Thread.currentThread().getName()); } }
Let me explain to you how this code is working. You see that the ThreadExample1 class extends the Thread class and overrides the run() method. Inside the run() method, it simply prints a message that includes the name of the thread, which is returned from the getName() method of the Thread class.
And now let’s see the main() method that is invoked when the program starts. It creates an instance of the ThreadExample1 class and call its start() method to put the thread into running state. And the last line prints a message that includes the name of the main thread - every Java program is started from a thread called main. The static method currentThread() returns the Thread object associated with the current thread.
Run this program and you will see the output as follows:
My name is: Thread-0 My name is: main
You see, there are actually 2 threads:
- Thread-0: is the name of the thread we created.
- main: is the name of the main thread that starts the Java program.
The thread Thread-0 terminates as soon as its run() method runs to complete, and the thread main terminates after the main() method completes its execution.
One interesting point is that, if you run this program again for several times, you will see sometimes the thread Thread-0 runs first, sometimes the thread main runs first. This can be recognized by the order of thread names in the output changes randomly. That means there’s no guarantee of which thread runs first as they are both started concurrently. You should bear in mind this behavior with regard to multi-threading context.
Now, let’s see the second way that uses the Runnable interface. In the code below, the ThreadExample2 class implements the Runnable interface and override the run() method:
public class ThreadExample2 implements Runnable { public void run() { System.out.println("My name is: " + Thread.currentThread().getName()); } public static void main(String[] args) { Runnable task = new ThreadExample2(); Thread t2 = new Thread(task); t2.start(); System.out.println("My name is: " + Thread.currentThread().getName()); } }
As you can see, there’s a small difference as compared to the previous program: An object of type Runnable (the ThreadExample2 class) is created and passed to the constructor of a Thread object (t2). The Runnable object can be viewed as a task which is separated from the thread that executes the task.
The two programs behave the same. So what are the pros and cons of these two ways of creating a thread?
Here’s the answer:
- Extending the Thread class can be used for simple cases. It cannot be used if your class needs to extend another class because Java doesn’t allow multiple inheritances of class.
- Implementing the Runnable interface is more flexible as Java allows a class can both extend another class and implement one or more interfaces.
And remember that the thread terminates after its run() method returns. It is put into dead state and cannot be able to start again. You can never restart a dead thread.
You can also set name for a thread either via constructor of the Thread class or via the setter method setName(). For example:
Thread t1 = new Thread("First Thread"); Thread t2 = new Thread(); t2.setName("Second Thread");
2. How to pause a thread
You can make the currently running thread pauses its execution by invoking the static method sleep(milliseconds) of the Thread class. Then the current thread is put into sleeping state. Here’s how to pause the current thread:
try { Thread.sleep(2000); } catch (InterruptedException ex) { // code to resume or terminate... }
This code pauses the current thread for about 2 seconds (or 2000 milliseconds). After that amount of time, the thread returns to continue running normally.
InterruptedException is a checked exception so you must handle it. This exception is thrown when the thread is interrupted by another thread.
Let’s see a full example. The following NumberPrint program is updated to print 5 numbers, each after every 2 seconds:
public class NumberPrint implements Runnable { public void run() { for (int i = 1; i <= 5; i++) { System.out.println(i); try { Thread.sleep(2000); } catch (InterruptedException ex) { System.out.println("I'm interrupted"); } } } public static void main(String[] args) { Runnable task = new NumberPrint(); Thread thread = new Thread(task); thread.start(); } }
Note that you can’t pause a thread from another thread. Only the thread itself can pause its execution. And there’s no guarantee that the thread always sleep exactly for the specified time because it can be interrupted by another thread, which is described in the next section.
3. How to interrupt a thread
Interrupting a thread can be used to stop or resume the execution of that thread from another thread. For example, the following statement interrupts the thread t1 from the current thread:
t1.interrupt();
If t1 is sleeping, then calling interrupt() on t1 will cause the InterruptedException to be thrown. And whether the thread should stop or resume depending on the handling code in the catch block.
In the following code example, the thread t1 prints a message after every 2 seconds, and the main thread interrupts t1 after 5 seconds:
public class ThreadInterruptExample implements Runnable { public void run() { for (int i = 1; i <= 10; i++) { System.out.println("This is message #" + i); try { Thread.sleep(2000); continue; } catch (InterruptedException ex) { System.out.println("I'm resumed"); } } } public static void main(String[] args) { Thread t1 = new Thread(new ThreadInterruptExample()); t1.start(); try { Thread.sleep(5000); t1.interrupt(); } catch (InterruptedException ex) { // do nothing } } }
As you can see in the catch block in the run() method, it continues the for loop when the thread is interrupted:
try { Thread.sleep(2000); } catch (InterruptedException ex) { System.out.println("I'm resumed"); continue; }
That means the thread resumes running while it is sleeping.
To stop the thread, just change the code in the catch block to return from the run() method like this:
try { Thread.sleep(2000); } catch (InterruptedException ex) { System.out.println("I'm about to stop"); return; }
You see, the return statement causes the run() method to return which means the thread terminates and goes to dead state.
What if a thread doesn’t sleep (no handling for InterruptedException)?
In such case, you need to check the interrupt status of the current thread using either of the following methods of the Thread class:
- interrupted(): this static method returns true if the current thread has been interrupted, or false otherwise. Note that this method clears the interrupt status, meaning that if it returns true, then the interrupt status is set to false.
- isInterrupted(): this non-static method checks the interrupt status of the current thread and it doesn’t clear the interrupt status.
The ThreadInterruptExample above can be modified to use the checking method as below:
public class ThreadInterruptExample implements Runnable { public void run() { for (int i = 1; i <= 10; i++) { System.out.println("This is message #" + i); if (Thread.interrupted()) { System.out.println("I'm about to stop"); return; } } } public static void main(String[] args) { Thread t1 = new Thread(new ThreadInterruptExample()); t1.start(); try { Thread.sleep(5000); t1.interrupt(); } catch (InterruptedException ex) { // do nothing } } }
However this version doesn’t behave the same as the previous one because the thread t1 terminates very quickly as it doesn’t sleep and the print statements are executed very fast. So this example is just to show you how it is used. In practice, this kind of checking on interrupt status should be applied for long-running operations such as IO, network, database, etc.
And remember that when the InterruptedException is thrown, the interrupt status is cleared.
If you look at the Thread class in Javadocs, you will see there are 4 methods:
destroy() - stop() - suspend() - resume()
However all these methods are deprecated, meaning that you shouldn’t use them. Let use the interruption mechanism I have described so far.
4. How to make a thread waits other threads (join)?
This is called joining and is useful in case you want the current thread to wait for other threads to complete. After that the current thread continues running. For example:
t1.join();
This statement causes the current thread to wait for the thread t1 to complete before it continues. In the following program, the current thread (main) waits for the thread t1 to complete:
public class ThreadJoinExample implements Runnable { public void run() { for (int i = 1; i <= 10; i++) { System.out.println("This is message #" + i); try { Thread.sleep(2000); } catch (InterruptedException ex) { System.out.println("I'm about to stop"); return; } } } public static void main(String[] args) { Thread t1 = new Thread(new ThreadJoinExample()); t1.start(); try { t1.join(); } catch (InterruptedException ex) { // do nothing } System.out.println("I'm " + Thread.currentThread().getName()); } }
In this program, the current thread (main) always terminates after the thread t1 completes. Hence you see the message “I’m main” is always printed last:
This is message #1 This is message #2 This is message #3 This is message #4 This is message #5 This is message #6 This is message #7 This is message #8 This is message #9 This is message #10 I'm main
Note that the join() method throws InterruptedException if the current thread is interrupted, so you need to catch it.
There are 2 overloads of join() method:
- join(milliseconds)
- join(milliseconds, nanoseconds)
These methods cause the current thread to wait at most for the specified time. That means if the time expires and the joined thread has not completed, the current thread continues running normally.
You can also join multiple threads with the current thread, for example:
t1.join(); t2.join(); t3.join();
In this case, the current thread has to wait for all three threads t1, t2 and t3 completes before it can resume running.
That’s the fundamentals of using threads in Java.
You are now able to create, start, interrupt (to stop or resume) and join (to wait) threads.
API References:
Related Java Thread Tutorials:
- How to list all threads currently running in Java
- Understanding Thread Group in Java
- Understanding Thread Priorities and Daemon Thread in Java
- Understanding Thread States (Thread Life Cycle) in Java
Other Java Concurrency Tutorials:
- Java Synchronization Tutorial
- Understanding Deadlock, Livelock and Starvation with Code Examples in Java
- Understanding Java Fork-Join Framework with Examples
- Understanding Java Thread Pool and Executors
Comments
Thank you.
This is a great Java Swing tutorial in which you can learn how to pause and resume a thread: codejava.net/.../...