Threads and Multithreading – Part 6

Threads - images of multicoloured threads

This is part 6 of this series. If you missed any of the last five, you can find them here:

Runnable vs Callable

In this post we’ll start looking at the some of the classes in the Concurrency API. But first we’ll revise what we know about the Runnable interface, and then introduce the Callable interface.

As we saw from the previous posts, creating running and controlling threads manually is tedious and error-prone. Running more than a few threads and keeping track of them can become problematic. This gets even more difficult to handle if we want to get results from the running threads. How can we easily do this if the run() method from the Runnable doesn’t even allow us to return a value directly?

Java 5 introduced the Concurrency API to simplify concurrent programming. It contains classes providing thread functionality that is tedious and/or difficult to implement in our own code, and makes it much easier to work with threads. Nearly every Java release since version 5 has added extra classes and methods to the API to make it even easier to use concurrency in our own code. The Concurrency API is in the java.util.concurrent package and its sub-packages.

Runnable Revision

Remember that there are two basic ways to create a class that can be run as a background thread: we can either extend the Thread class or we can implement the Runnable interface. The Thread class has a run() method which we must then override to get our required functionality. The Runnable interface defines a run() method which we need to implement.

The preferred way to create a thread is to implement the Runnable interface in an existing class. Here is the Runnable interface:

@FunctionalInterface
public interface Runnable {
    public void run();
}

Implementing Runnable is easy:

public class MyClass extends SomeOtherClass implements Runnable {

    // data and code relevant to MyClass 

    // implementing the run() method of the Runnable interface
    @Override
    public void run() {
        // statements;
    }
}

The Runnable interface is a functional interface with a single abstract method. As such, a Runnable variable can be used as the assignment target for a lambda expression or a method reference.

Let’s create a lambda expression and assign it to a Runnable variable. We can add this code to a main() method somewhere to test it:

Runnable task = () -> {
    String threadName = Thread.currentThread().getName();
    System.out.println("Hello from " + threadName);
};

// this runs in the main thread, and not as a background thread
task.run();

// this runs as a separate background thread 
Thread thread = new Thread(task);
thread.start();

System.out.println("Done!");

The result on the console might look like this:

Hello main
Hello Thread-0
Done!

Or like this:

Hello main
Done!
Hello Thread-0

Due to the two threads running concurrently, we can’t predict if the Runnable object will be invoked before or after the main thread prints “Done!”. The order is non-deterministic. This means we cannot determine beforehand what the result will be. This is what makes concurrent programming so complex.

Callable Interface

The Runnable interface is very useful, but it has a few limitations: the run() method cannot return a value, nor can it throw any exceptions. Both are primary ways that we use to check whether a method is successful in doing whatever it needs to do. A method usually returns a value if successful, or throws an exception if not.

This is where the Callable interface comes into the picture. The Callable interface represents a task that can return a result and can throw an exception. Here is the Callable interface:

@FunctionalInterface
public interface Callable <V> {
    public V call() throws Exception;
} 

The Callable interface is similar to Runnable inasmuch as instances of either can be executed by a thread. The difference is that a Callable object can return a parameterized result and can throw an exception.

If we implement this interface, we must define a single method with no arguments called call() returning the parameterized value.

Implementing Callable is almost as easy as implementing Runnable. Here is an example of a call() method returning an int value wrapped as an Integer:

import java.util.concurrent.Callable;

public class MyClass extends SomeOtherClass implements Callable<Integer> {

    // data and code relevant to MyClass 

    // implementing the call() method of the Callable interface
    @Override
    public Integer call() {
        // statements;
        return 42;  // using auto-boxing
    }
}

Because Callable is a functional interface, we can also use a lambda expression to define the work to be done by the Callable instance. The following Callable lambda expression returns an Integer after sleeping for a second. This is to simulate a long running task.

Callable<Integer> task = () -> {
    try {
        Thread.sleep(1000); // 1000 milliseconds
        return 42;
    }
    catch (InterruptedException e) {
        throw new IllegalStateException("Task interrupted!", e);
    }
};

Running a Callable Object

As we’ve already seen, Runnable objects can be passed to Thread constructors to be run. Callable objects are different. They cannot be run directly by thread instances. They need to be submitted to an ExecutorService implementation to be run.

In the next post we’ll look at implementations of the Executor and ExecutorService interfaces, and how to submit Callable tasks to them. We’ll also look into the future with the Future interface. A Future wraps the returned value of a Callable task which still has to run.

If you’ve been following this series, I hope you are feeling more confident about threads. Please share your comments and questions.

Until then, stay safe and keep learning!

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.