Over a million developers have joined DZone.

An Informal Approach to Higher-Order Runnables

In this detailed tutorial, Federico Peralta Schaffner details the mechanics of higher-order functions, including the higher-order runnable in Java 8.

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

A higher-order function is a function that takes one or more functions as arguments and that might return a function as its result. Here we take an informal approach to higher-order functions by showing how to apply this functional programming concept to Runnable instances.

Higher-Order Runnables

Background

In Java 8, a block of code that takes no arguments and has no returning value can be assigned to a Runnable instance straightforwardly:

Runnable blockOfCode = () -> {
    System.out.println("Q: What was the weather like in winter of 2015?");
    System.out.println("A: It was very cold");
};

Then, later in our program, we could execute this block of code by invoking the run() method of our Runnable instance:

blockOfCode.run();

Now, let's suppose the code resides in the whatWasTheWeatherLike() method of theWinter class:

public class Winter {

    private final int year;

    public Winter(int year) {
        this.year = year;
    }

    public void whatWasTheWeatherLike() {
        System.out.printf("Q: What was the weather like in winter of %d?%n", year);
        System.out.println("A: It was very cold");
    }
}

In this case, instead of using a lambda expression with an inline block of code, we could use a reference to an instance method of the Winter class:

Winter winterOf2015 = new Winter(2015);
Runnable runnable = winterOf2015::whatWasTheWeatherLike;

But what if we wanted to assign different blocks of code to different Runnable instances? We might want to keep references not only to the whatWasTheWeatherLike() instance method of the winterOf2015 instance, but also to any no-arguments, void method of any class, as well as to any inline block of code:

List runnables = new ArrayList<>();

Winter winterOf2015 = new Winter(2015);
runnables.add(winterOf2015::whatWasTheWeatherLike);

Winter winterOf2012 = new Winter(2012);
runnables.add(winterOf2012::whatWasTheWeatherLike);

runnables.add(() -> {
    System.out.println("Inline block of code");
});

// will print a blank line when executed
runnables.add(System.out::println);

// will invoke someNoArgsVoidMethod method on
// someClassInstance instance when executed
SomeClass someClassInstance = new SomeClass();
runnables.add(someClassInstance::someNoArgsVoidMethod);

// will exit the program when executed
runnables.add(() -> System.exit(0));

We have stored all the Runnable instances above in the runnables list. To execute them, we could iterate the list and invoke the run() method on each element:

for (Runnable r : runnables) {
    r.run();
}

Or in a one-liner:

runnables.forEach(Runnable::run);

However, this will execute the Runnable instances sequentially and in the same order they were added to the list. What if we wanted to execute them in parallel, or some sequentially and some in parallel? And what if we had more than one list and wanted to execute their Runnable elements interleaved? This is when higher-order functions come in handy.

Higher-Order Functions

The definition of higher-order function I most like states that "A higher-order function is a function that takes one or more functions as arguments and that might return a function as its result."

While the above definition is quite easy to understand for a programmer that has worked with functional constructs, I think it could be a little bit too much functional for mainstream Java developers. On the other hand, the Runnable interface is widely known in the Java world. So, why not use it to show how useful higher-order functions can be?

We define a higher-order runnable as per the definition given above, except for the term function, which we'll change by runnable. Thus, according to our new, slightly modified definition, a higher-order runnable is "a function that takes one or more Runnable instances as arguments and that returns a Runnable instance as its result."

In code, we represent a HigherOrderRunnable with the following interface:

@FunctionalInterface
public interface HigherOrderRunnable {

    /**
     * Combines the given Runnables into a single Runnable.
     *
     * @param runnables Runnables to be combined
     * @return A Runnable that is the result of combining the given Runnables
     */
    Runnable combine(Runnable... runnables);
}

The HigherOrderRunnable interface has a single abstract method (SAM) that receives an array of Runnable arguments and returns a Runnable instance as its result. The fact that it has only one abstract method makes it a functional interaface, which means we can implement it by assigning the following lambda expression to it:

HigherOrderRunnable higherOrderRunnable = (Runnable... runnables) -> {
    Runnable result = new Runnable() {
        @Override
        public void run() {
            for (Runnable r : runnables) {
                r.run();
            }
        }
    };
    return result;
};

This lambda expression consists of a Runnable varargs parameter (named runnables) and a body in which a new Runnable instance (named result) is created and returned.

Within result's run() method (which is implemented by means of an anonymous inner class), runnables is iterated and the run() method is invoked on each one of its Runnable elements. Simply put, we are returning a Runnable instance that, when executed, will execute the given Runnable instances sequentially.

We have created our first higher-order runnable!

But we can do it better. As Runnable is also a functional interface (its run() method is a single abstract method), we can replace the anonymous inner class with a nested lambda expression:

HigherOrderRunnable higherOrderRunnable = (Runnable... runnables) -> {
    Runnable result = () -> {
        for (Runnable r : runnables) {
            r.run();
        }
    };
    return result;
};

While that's better, there's still room for improvement. Instead of the for loop, we could create a Stream from the runnables vargars parameter and execute each one of its Runnable elements with the forEach() terminal operation:

HigherOrderRunnable higherOrderRunnable = (Runnable... runnables) -> {
    Runnable result = () -> {
        Arrays.stream(runnables).forEach(Runnable::run);
    };
    return result;
};

If we also inline the result variable, remove the useless braces and let the compiler do its type inference work, we get to the following equivalent, minimal construct:

HigherOrderRunnable higherOrderRunnable = 
    runnables -> () -> Arrays.stream(runnables).forEach(Runnable::run);

Starting from Java 8, interfaces are allowed to have static methods. I'll take advantage of this feature to declare the sequential() factory method on our HigherOrderRunnable functional interface, which will contain the above lambda expression:

@FunctionalInterface
public interface HigherOrderRunnable {

    Runnable combine(Runnable... runnables);

    static HigherOrderRunnable sequential() {
        return runnables -> () -> Arrays.stream(runnables).forEach(Runnable::run);
    }
}

If we had three Runnable instances, i.e. r1, r2 and r3, we could use our HigherOrderRunnable interface as follows:

HigherOrderRunnable sequential = HigherOrderRunnable.sequential();

Runnable runnable = sequential.combine(r1, r2, r3);

runnable.run();

When runnable.run() is invoked, r1, r2 and r3 will be executed sequentially.

Before taking our higher-order runnables to the next level, let's introduce the Sleeper enum, which I'll use in the rest of this article to simulate long-running tasks:

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * Enum that simply sleeps; TWO sleeps 2 seconds, THREE sleeps 3 seconds, etc.
 */
public enum Sleeper {
    TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7);

    private final int seconds;

    private Sleeper(int seconds) {
        this.seconds = seconds;
    }

    /**
     * Sleeps the number of seconds specified by this enum's
     * seconds attribute, generating a countdown output.
     */
    public void sleep() {
        System.out.printf("%s => Sleeping %d seconds...%n", this, seconds);
        // Countdown iteration: n, n - 1, ..., 0
        IntStream.iterate(seconds, i -> i - 1).limit(seconds).forEach(i -> {
            try {
                System.out.printf("%s => %d%n", this, i);
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                // Propagate interrupt status
                Thread.currentThread().interrupt();
                System.out.printf("%s => Interrupted!%n", this);
            }
        });
        System.out.printf("%s => 0. Waking up...%n", this);
    }
}

The Sleeper.sleep() method uses the IntStream.iterate() method to generate a stream of descending int elements that are printed in a countdown fashion, i.e. Sleeper.TWO.sleep() produces the 2, 1, 0 countdown sequence, Sleeper.THREE.sleep() produces 3, 2, 1, 0, etc. The Sleeper.sleep() method actually takes n seconds to execute (with n being the starting number) and gently prints the countdown sequence.

Let's use the Sleeper enum to see our sequential HigherOrderRunnable in action:

public class SequentialHigherOrderRunnableExample {

    public static void demo() {
        // Create a sequential higher-order runnable, that will combine
        // runnables by making them run in sequence
        HigherOrderRunnable sequential = HigherOrderRunnable.sequential();

        // Use the sequential higher-order runnable to combine all our runnables
        // (Note that the result of combining all our runnables is also a
        // runnable)
        Runnable sequence2_7 = sequential.combine(
                Sleeper.TWO::sleep,
                Sleeper.THREE::sleep,
                Sleeper.FOUR::sleep,
                Sleeper.FIVE::sleep,
                Sleeper.SIX::sleep,
                Sleeper.SEVEN::sleep);

        System.out.println("-----------------------------------------");
        System.out.println("SEQUENTIAL");
        System.out.println("-----------------------------------------");

        long init = System.currentTimeMillis();

        // Run our fresh new runnable
        sequence2_7.run();

        long end = System.currentTimeMillis();

        System.out.println("-----------------------------------------");
        System.out.println("Total elapsed time: " + (end - init) / 1_000 + " seconds");
        System.out.println("-----------------------------------------");
    }
}

We're using our HigherOrderRunnable to combine six Runnable instances, which are defined by Sleeper.TWO::sleep to Sleeper.SEVEN::sleep method references. Executing the demo() method above produces the following output:

-----------------------------------------
SEQUENTIAL
-----------------------------------------
TWO => Sleeping 2 seconds...
TWO => 2
TWO => 1
TWO => 0. Waking up...
THREE => Sleeping 3 seconds...
THREE => 3
THREE => 2
THREE => 1
THREE => 0. Waking up...
FOUR => Sleeping 4 seconds...
FOUR => 4
FOUR => 3
FOUR => 2
FOUR => 1
FOUR => 0. Waking up...
FIVE => Sleeping 5 seconds...
FIVE => 5
FIVE => 4
FIVE => 3
FIVE => 2
FIVE => 1
FIVE => 0. Waking up...
SIX => Sleeping 6 seconds...
SIX => 6
SIX => 5
SIX => 4
SIX => 3
SIX => 2
SIX => 1
SIX => 0. Waking up...
SEVEN => Sleeping 7 seconds...
SEVEN => 7
SEVEN => 6
SEVEN => 5
SEVEN => 4
SEVEN => 3
SEVEN => 2
SEVEN => 1
SEVEN => 0. Waking up...
-----------------------------------------
Total elapsed time: 27 seconds
-----------------------------------------

Invoking run() on sequence2_7 instance takes 27 seconds, which is expected, since the supplied Runnable instances are executed sequentially and 2 + 3 + 4 + 5 + 6 + 7 = 27.

So far so good. But what if we wanted to execute our Runnable instances in parallel? Let's implement a higher-order runnable that accomplishes this:

static HigherOrderRunnable parallel(ExecutorService executor) {
    return runnables -> () -> {
        try {
            CountDownLatch latch = new CountDownLatch(runnables.length);
            Arrays.stream(runnables).forEach(task -> executor.submit(() -> {
                try {
                    task.run();
                } finally {
                    latch.countDown();
                }
            }));
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    };
}

Here we are creating a lambda expression that takes a varargs parameter of Runnable instances (named runnables). This lambda expression returns a Runnable instance that, when executed, will submit each element of runnables to the provided executor service. A CountDownLatch is used to wait until all of them complete their execution.

Note that the Runnable instance we are submitting to the executor service is a wrapper around each Runnable element of runnables. This wrapper is implemented by means of a lambda expression in which we only do two things: first, we execute the actual Runnable instance (named task) and then, we immediately decrement the count of the latch. In order to decrement the latch even if the task.run() method throws an exception, we're using a try/finally block.

Let's test the above higher-order runnable with our Sleeper enum:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ParallelHigherOrderRunnableExample {

    public static void demo() {
        // Create an executor capable of running 6 tasks in parallel
        ExecutorService executor = Executors.newFixedThreadPool(6);

        // Create a parallel higher-order runnable, that will combine runnables 
        // by making them run in parallel
        HigherOrderRunnable parallel = HigherOrderRunnable.parallel(executor);

        // Use the parallel higher-order runnable to combine all our runnables
        // (Note that the result of combining all our runnables is also a
        // runnable)
        Runnable parallel2_7 = parallel.combine(
                Sleeper.TWO::sleep,
                Sleeper.THREE::sleep,
                Sleeper.FOUR::sleep,
                Sleeper.FIVE::sleep,
                Sleeper.SIX::sleep,
                Sleeper.SEVEN::sleep);

        System.out.println("-----------------------------------------");
        System.out.println("PARALLEL");
        System.out.println("-----------------------------------------");

        long init = System.currentTimeMillis();

        // Run our fresh new runnable
        parallel2_7.run();

        long end = System.currentTimeMillis();

        System.out.println("-----------------------------------------");
        System.out.println("Total elapsed time: " + (end - init) / 1_000 + " seconds");
        System.out.println("-----------------------------------------");

        // Release executor resources
        executor.shutdown();
    }
}

Executing the demo() method above produces the following output in my laptop:

-----------------------------------------
PARALLEL
-----------------------------------------
TWO => Sleeping for 2 seconds...
SEVEN => Sleeping for 7 seconds...
SIX => Sleeping for 6 seconds...
FIVE => Sleeping for 5 seconds...
FOUR => Sleeping for 4 seconds...
THREE => Sleeping for 3 seconds...
SEVEN => 7
FIVE => 5
TWO => 2
SIX => 6
FOUR => 4
THREE => 3
FIVE => 4
SEVEN => 6
THREE => 2
TWO => 1
SIX => 5
FOUR => 3
THREE => 1
SEVEN => 5
FIVE => 3
TWO => 0. Waking up...
SIX => 4
FOUR => 2
THREE => 0. Waking up...
SEVEN => 4
FIVE => 2
FOUR => 1
SIX => 3
FIVE => 1
SEVEN => 3
FOUR => 0. Waking up...
SIX => 2
FIVE => 0. Waking up...
SEVEN => 2
SIX => 1
SEVEN => 1
SIX => 0. Waking up...
SEVEN => 0. Waking up...
-----------------------------------------
Total elapsed time: 7 seconds
-----------------------------------------

Invoking run() on the parallel2_7 instance takes now only 7 seconds, which is expected, since the Runnable instance that takes longest to execute is Sleeper.SEVEN::sleep. The output also shows that all Runnable instances start at once and that they wake up in order, as per their duration (Sleeper.TWO::sleep wakes up first, then Sleeper.THREE::sleep, and so on).

Up until now, we've used our higher-order runnables to execute a list of Runnable instances either sequentially or in parallel. However, their real power resides in the essential fact that they're not only higher-order, but also Runnable instances. As such, they can be subsequently combined with other Runnable instances, producing a new Runnable result, which in turn can be combined with more Runnable instances to produce another Runnable result, etc. In the following code, I'll show how this technique can be used to execute some Runnable instances sequentially and some in parallel:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MixedHigherOrderRunnableExample {

    public static void demo() {
        // Create a sequential higher-order runnable, that will combine
        // runnables by making them run in sequence
        HigherOrderRunnable sequential = HigherOrderRunnable.sequential();

        // Use the sequential higher-order runnable to combine our even
        // runnables first, and then to combine our odd runnables
        // (Note that the result of combining our even runnables is also a
        // runnable, and the same occurs with our odd runnables)
        Runnable sequenceEven = sequential.combine(
                Sleeper.TWO::sleep,
                Sleeper.FOUR::sleep,
                Sleeper.SIX::sleep);
        Runnable sequenceOdd = sequential.combine(
                Sleeper.THREE::sleep,
                Sleeper.FIVE::sleep,
                Sleeper.SEVEN::sleep);

        // Create an executor capable of running 2 tasks in parallel
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // Create a parallel higher-order runnable, that will combine
        // runnables by making them run in parallel
        HigherOrderRunnable parallel = HigherOrderRunnable.parallel(executor);

        // Use the parallel higher-order runnable to combine our previously
        // combined even runnables with our previously combined odd runnables
        // (Note that the result of combining these two runnables is also a
        // runnable)
        Runnable mixed = parallel.combine(sequenceEven, sequenceOdd);

        System.out.println("-----------------------------------------");
        System.out.println("MIXED");
        System.out.println("-----------------------------------------");

        long init = System.currentTimeMillis();

        // Run our fresh new runnable
        mixed.run();

        long end = System.currentTimeMillis();

        System.out.println("-----------------------------------------");
        System.out.println("Total elapsed time: " + (end - init) / 1_000 + " seconds");
        System.out.println("-----------------------------------------");

        // Release executor resources
        executor.shutdown();
    }
}

On the one hand, we're combining our even Runnable instances with a sequential higher-order runnable, and on the other hand, we're using the same sequential higher-order runnable to combine our odd Runnable instances. Then, we're using a parallel higher-order runnable to combine the Runnable instances previously returned by the sequential higher-order runnable. This produces the following output in my laptop:

-----------------------------------------
MIXED
-----------------------------------------
TWO => Sleeping for 2 seconds...
THREE => Sleeping for 3 seconds...
THREE => 3
TWO => 2
THREE => 2
TWO => 1
THREE => 1
TWO => 0. Waking up...
FOUR => Sleeping for 4 seconds...
FOUR => 4
THREE => 0. Waking up...
FIVE => Sleeping for 5 seconds...
FIVE => 5
FOUR => 3
FIVE => 4
FOUR => 2
FIVE => 3
FOUR => 1
FOUR => 0. Waking up...
FIVE => 2
SIX => Sleeping for 6 seconds...
SIX => 6
FIVE => 1
SIX => 5
FIVE => 0. Waking up...
SEVEN => Sleeping for 7 seconds...
SIX => 4
SEVEN => 7
SIX => 3
SEVEN => 6
SIX => 2
SEVEN => 5
SIX => 1
SEVEN => 4
SIX => 0. Waking up...
SEVEN => 3
SEVEN => 2
SEVEN => 1
SEVEN => 0. Waking up...
-----------------------------------------
Total elapsed time: 15 seconds
-----------------------------------------

Invoking run() on the mixed instance takes 15 seconds. This is to be expected, since, amongst the Runnable instances that are executed in parallel, the one that takes longest is sequenceOdd, which in turn is the result of combining Sleeper.THREE::sleep, Sleeper.FIVE::sleep and Sleeper.SEVEN::sleep sequentially. And 3 + 5 + 7 = 15, matching our total duration.

Finally, here is a class that launches the three demos shown above:

public class HigherOrderRunnablesLauncher {

    public static void main(String[] args) {

        // Run sequential runnables combinator demo
        SequentialHigherOrderRunnableExample.demo();

        // Run parallel runnables combinator demo
        ParallelHigherOrderRunnableExample.demo();

        // Run sequential + parallel runnables combinator demo
        MixedHigherOrderRunnableExample.demo();
    }
}

I've uploaded all the code shown here to a GitHub repo. Please contact me or leave me a comment if you find any issues.

And this is how my very first (and quite long) article comes to an end. I hope you've enjoyed reading it as much as I've enjoyed writing it.

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
java 8 ,functional programming ,higher-order functions ,concurrency ,java

Published at DZone with permission of Federico Peralta Schaffner, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}