Java Threads Definitive Tutorial

Java Thread is a basic of a multithreading program.

This article is the first part of Java concurrency topics.

I’ll give answers to the following interview questions:

  • How to create a thread in Java?
  • What is a Thread Lifecycle?
  • How to prioritize thread execution?
  • How to stop a thread in Java?
  • What is a daemon thread?
  • How to handle InterruptedException?
  • How to handle exceptions outside of the thread?

I’ll write a simple thread program in Java to show how does it work.

Using Threads in Java

Programmers are using threads in Java to execute a piece of code in an asynchronous way.

There are 2 ways how to create a thread in Java:

  • Create a child of Thread class
  • Implement Runnable interface

The 2nd one is a more flexible way because you don’t have inheritance restrictions.

Create a Sub-class of a Thread

Let’s implement a TransactionThread that extends Thread class.

Imagine it executes bank transaction.

It accepts transaction id via the constructor and I want to print it during execution.

Example:

package com.explainjava;
 
public class TransactionThread extends Thread {
 
    private final int id;
 
    public TransactionThread(int id) {
        this.id = id;
    }
 
    @Override
    public void run() {
        System.out.println("EXECUTE CONCURRENT TRANSACTION #" + id);
    }
}

You should override method run() and put your code there.

I want to execute 10 concurrent transactions.

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        new TransactionThread(i).start();
    }
}

i variable is an id of the transaction.

Output:

EXECUTE CONCURRENT TRANSACTION #0
EXECUTE CONCURRENT TRANSACTION #4
EXECUTE CONCURRENT TRANSACTION #3
EXECUTE CONCURRENT TRANSACTION #2
EXECUTE CONCURRENT TRANSACTION #6
EXECUTE CONCURRENT TRANSACTION #1
EXECUTE CONCURRENT TRANSACTION #7
EXECUTE CONCURRENT TRANSACTION #5
EXECUTE CONCURRENT TRANSACTION #8
EXECUTE CONCURRENT TRANSACTION #9

As you can see threads were executed in a different order.

JVM doesn’t guarantee threads execution order.

Implement a Runnable interface

Another way to create a new thread is to implement Runnable interface.

It’s doing the same as in the example above.

package com.explainjava;
 
public class TransactionRunner implements Runnable {
 
    private final int id;
 
    public TransactionRunner(int id) {
        this.id = id;
    }
 
    @Override
    public void run() {
        System.out.println("EXECUTE CONCURRENT TRANSACTION #" + id);
    }
}

Execution looks like this:

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        Thread thread = new Thread(new TransactionRunner(i));
        thread.start();
    }
}

Output:

EXECUTE CONCURRENT TRANSACTION #0
EXECUTE CONCURRENT TRANSACTION #4
EXECUTE CONCURRENT TRANSACTION #5
EXECUTE CONCURRENT TRANSACTION #3
EXECUTE CONCURRENT TRANSACTION #6
EXECUTE CONCURRENT TRANSACTION #2
EXECUTE CONCURRENT TRANSACTION #1
EXECUTE CONCURRENT TRANSACTION #8
EXECUTE CONCURRENT TRANSACTION #7
EXECUTE CONCURRENT TRANSACTION #9

Again threads executed in a different order.

How to Create a Thread in Java 8

In general, it’s a combination of implementing Runnable interface and Java 8 lambda.

Example:

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        int transactionId = i;
        Thread thread = new Thread(() -> System.out.println("EXECUTE CONCURRENT TRANSACTION #" + transactionId));
        thread.start();
    }
}

Looks even better than first two solutions, but it’s a little bit complicated to test piece of code inside of the thread.

If you have encapsulated code in separate class you can easily write unit tests for it.

I copied i variable because local variables referenced from a lambda expression must be final or effectively final.

Java Thread Lifecycle

There are 6 possible thread states in Java.

NewThread is created but not started yet.
RunnableThread is executing, but it may be waiting for system resources, e.g. processor.
BlockedThread is waiting for monitor lock to enter a synchronized block or method.
WaitingThread is waiting for another thread action. It can happen if you’re using: Object.wait(), Thread.join() or LockSupport.part() methods. For example. a thread called Object.wait() on an object is waiting for another thread call Object.notify() or Object.notifyAll() on that object.
Timed WaitingThe same as waiting but with specified waiting time. The following methods can do that: Thread.sleep(), Object.wait(long), Thread.join(long), LockSupport.parkNanos() and LockSupport.partUntil()
TerminatedExecution is finished

So lifecycle can be the following:

  1. New
  2. Runnable
  3. Blocked, waiting or timed waiting
  4. Terminated

3rd point is optional.

How to Set Thread Priority

As I mentioned before JVM doesn’t guarantee threads execution order.

One way to impact an order is to specify a priority.

You can invoke method:

public final void setPriority(int newPriority)

So threads with a higher priority are executed in preference to threads with lower priority.

Thread class defined 3 constants:

/**
* The minimum priority that a thread can have.
*/
public static final int MIN_PRIORITY = 1;
 
/**
* The default priority that is assigned to a thread.
*/
public static final int NORM_PRIORITY = 5;
 
/**
* The maximum priority that a thread can have.
*/
public static final int MAX_PRIORITY = 10;

As you can see minimum priority is 1 and maximum is 10.

If you invoke setPriority(int newPriority) method with out of the range value – IllegalArgumentException will be thrown.

Max thread priority can’t be higher than thread group max priority as well.

How to Stop Thread

The preferable way to stop a thread in Java is to use isInterrupted() and interrupt() methods of a Thread class.

Example:

package com.explainjava;
 
public class ThreadRunner implements Runnable {
 
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("EXECUTE THREAD");
        }
    }
}

We have a code that executes all time while the thread is not interrupted.

I want to start a thread, wait a little bit until it’s working and then stop it.

public static void main(String[] args) throws InterruptedException {
    System.out.println("START THREAD");
    Thread thread = new Thread(new ThreadRunner());
    thread.start();
 
    System.out.println("WAIT A LITTLE BIT");
    Thread.sleep(100L);
 
    thread.interrupt();
    System.out.println("THREAD IS STOPPED");
}

So the mechanism is simple:

  • Use isInterrupted() method inside of your thread to check is current thread interrupted or not
  • Invoke thread.interrupt() when you want to stop a thread

What Is a Daemon Thread?

Your program will work until at least one thread is alive.

But what if I want to stop the program when the main thread is stopped?

You have to mark a thread as a daemon.

Example:

package com.explainjava;
 
public class ThreadRunner implements Runnable {
 
    @Override
    public void run() {
        while (true) {
            System.out.println("EXECUTE THREAD");
        }
    }
 
    public static void main(String[] args) {
        System.out.println("START THREAD");
        Thread thread = new Thread(new ThreadRunner());
        thread.start();
    }
}

This program will never stop.

You should do it manually.

To fix it we need to set daemon as true:

public static void main(String[] args) {
    System.out.println("START THREAD");
    Thread thread = new Thread(new ThreadRunner());
    thread.setDaemon(true);
    thread.start();
}

Now the new thread is marked as a daemon and it will finish its work when the main thread will be finished.

How To Catch Exception From Another Thread

For example, you started a thread and you want to handle an exception that occurred inside.

You can do it using uncaught exception handler.

Example:

package com.explainjava;
 
public class ThreadRunner implements Runnable {
 
    @Override
    public void run() {
        throw new IllegalArgumentException("Something went wrong");
    }
 
    public static void main(String[] args) {
        System.out.println("START THREAD");
        Thread thread = new Thread(new ThreadRunner());
        thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("EXCEPTION MESSAGE: " + e.getMessage());
            }
        });
        thread.start();
    }
}

Thread throws an exception during execution.

We catch this exception in UncaughtExceptionHandler and print its message.

What Is a Java InterruptedException And How To Handle It?

This exception is thrown when a thread is waiting or sleeping and thread is interrupted during or before this process.

I found 287 methods that throw this exception in Java 9.

The most important methods that you should know is:

  • Object.wait()
  • Thread.sleep()
  • Thread.joint()

The best practice to handle it is to mark a current thread as interrupted.

Example:

package com.explainjava;
 
public class ThreadRunner implements Runnable {
 
    @Override
    public void run() {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }
}

Conclusion

I explained the most important information about threads in Java, but the most complicated part is synchronization between multiple threads.

I’ll write about it in one of the future articles.

I hope you’ve got answers to the most popular interview questions about threads in Java.

If not – ask questions in comments.

Leave a Comment