Ripe times

Lean Concurrency in Java 8

Lukas Eder
ripe

Lambdas and improved APIs in Java 8 will make writing concurrent code that little bit easier. Let’s take a closer look.

This post was originally published over at jooq.org as part of a special series
focusing on all things Java 8, including how take advantage of
lambda expressions, extension methods, and other great
stuff. You’ll find the
source code on GitHub
.

Lean Concurrency in Java 8

Someone once said that (unfortunately, we don’t have the source
anymore):

Junior programmers think concurrency is hard.
Experienced programmers think concurrency is easy.
Senior programmers think concurrency is hard.

That is quite true. But on the bright side, Java 8 will at least
improve things by making it easier to write concurrent code with
lambdas and the many improved APIs. Let’s have a closer look:

Java 8 improving on JDK 1.0 API

java.lang.Thread has
been around from the very beginning in JDK 1.0. So
has java.lang.Runnable,
which is going to be annotated withFunctionalInterface in
Java 8.

It is almost a no-brainer how we can finally
submit Runnables to
Threadfrom now on. Let’s assume we have a
long-running operation:

public static int longOperation() {
    System.out.println("Running on thread #"
       + Thread.currentThread().getId());
 
    // [...]
    return 42;
}

We can then pass this operation
to 
Threads in various ways, e.g.

Thread[] threads = {
 
    // Pass a lambda to a thread
    new Thread(() -> {
        longOperation();
    }),
 
    // Pass a method reference to a thread
    new Thread(ThreadGoodies::longOperation)
};
 
// Start all threads
Arrays.stream(threads).forEach(Thread::start);
 
// Join all threads
Arrays.stream(threads).forEach(t -> {
    try { t.join(); }
    catch (InterruptedException ignore) {}
});

As we’ve mentioned in our
previous blog post, it’s a shame that lambda expressions did not
find a lean way to work around checked exceptions. None of the
newly added functional interfaces in the 
java.util.functionpackage
allow for throwing checked exceptions, leaving the work up to the
call-site.


In our last post
, we’ve thus publishedjOOλ (also jOOL, jOO-Lambda),
which wraps each one of the JDK’s
functional
interfaces in an equivalent functional interface that allows for
throwing checked exceptions. This is particularly useful with old
JDK APIs, such as JDBC, or the above Thread API. With jOOλ, we can then write:

// Join all threads
Arrays.stream(threads).forEach(Unchecked.consumer(
    t -> t.join()
));

Java 8 improving on Java 5 API

Java’s multi-threading APIs had been pretty dormant up until the
release of Java 5′s awesome ExecutorService.
Managing threads had been a burden, and people needed external
libraries or a J2EE / JEE container to manage thread pools. This
has gotten a lot easier with Java 5. We can nowsubmit a Runnable or
Callable to
an ExecutorService,
which manages its own thread-pool.

Here’s an example how we can leverage these Java 5 concurrency
APIs in Java 8:

ExecutorService service = Executors
    .newFixedThreadPool(5);
 
Future[] answers = {
    service.submit(() -> longOperation()),
    service.submit(ThreadGoodies::longOperation)
};
 
Arrays.stream(answers).forEach(Unchecked.consumer(
    f -> System.out.println(f.get())
));

Note, how we again use
an UncheckedConsumer from jOOλ to wrap the checked
exception thrown from the get() call in
RuntimeException.

Parallelism and ForkJoinPool in Java 8

Now, the Java 8 Streams API changes a lot of things in terms of
concurrency and parallelism. In Java 8, you can write the
following, for instance:

Arrays.stream(new int[]{ 1, 2, 3, 4, 5, 6 })
      .parallel()
      .max()
      .ifPresent(System.out::println);
While it isn’t necessary in this particular case, it’s still
interesting to see that the mere calling of 
parallel() will
run the 
IntStream.max()ForkJoinPoolwithout
you having to worry about the
involved 
ForkJoinTasks. This can be
really useful, as 
not everybody
welcomed the JDK 7 ForkJoin API the complexity it has
introduced
.

Read more about Java
8′s parallel streams in this interesting InfoQ article
.

Author
Lukas Eder
Lukas is a Java and SQL aficionado. He’s the founder and head of R&D at Data Geekery GmbH, the company behind jOOQ, the best way to write SQL in Java.
Comments
comments powered by Disqus