Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala async vs. Java ForkJoinTask

Some time ago I discovered the Scala Async Project. The question is: what is so magical in this async blocks which can not be implemented via plain functions (without macro expansion)?

Let's look at the first example from the introduction:

import ExecutionContext.Implicits.global
import scala.async.Async.{async, await}

val future = async {
    val f1 = async { ...; true }
    val f2 = async { ...; 42 }
    if (await(f1)) await(f2) else 0
}

I do not see anything in the above example which can not be written in pure Java. This code does exactly the same thing:

import java.util.concurrent.*;
import java.util.function.Supplier;

// First define a helper method for creating async blocks:
public static <T> ForkJoinTask<T> async(Supplier<T> supplier) {
    return new RecursiveTask<T>() {
        @Override
        protected T compute() {
            return supplier.get();
        }
    }.fork();
}

ForkJoinTask<Integer> future = ForkJoinPool.commonPool().submit(() -> {
    ForkJoinTask<Boolean> f1 = async(() -> true);
    ForkJoinTask<Integer> f2 = async(() -> 42);

    if (f1.join()) {
        return f2.join();
    } else {
        return 42;
    }
});

What can Scala async do that Java can't? Maybe in case of some more complex scenarios? What do I miss?

like image 288
ZhekaKozlov Avatar asked Mar 25 '14 18:03

ZhekaKozlov


1 Answers

There is one crucial difference in how the two snippets you posted work under the hood: blocking operations.

The scala-async snippet is roughly equivalent to:

val future = {
  val f1 = Future { ...; true }
  val f2 = Future { ...; 42 }
  f1.flatMap { b =>
    if(b) f2 else Future.successful(0)
  }
}

This is a callback-based code. There are no operations in there that would block any thread. Only future wrapping and callback registering (which happens under the hood of flatMap in this case). In other words, everything is asynchronous there.

On the other hand, the join method from Java's fork-join pool does block the thread.

No blocking operations is a significant performance/scalability advantage, because - greatly simplifying - no blocking => less threads needed => less OS resources needed + less context switching.

Summarizing: the purpose of scala-async is to make non-blocking, callback based, asynchronous processing as natural in its syntax as the standard, blocking approach (like the one you used in Java).

like image 124
ghik Avatar answered Nov 16 '22 02:11

ghik