Search code examples
javamultithreadingscalajvm-hotspot

How does scala's actor model make use of C threads and native system threads?


How does scala's actor model make use of C threads and native system threads? I understand that the compiler is plugable , so a scala compiler replaces the java compilers. I'm confused by the next step, doesn't the Intermediate Representation of the code just get optimised by the hotspot (possibly less effectively than the thread based model) and translated to a thread-based combination of machine code and C.


Solution

  • A very rough picture of how native threads are used by Akka is as follows.

    1. Akka Actors are objects instantiated by an ActorSystem
    2. Then they are scheduled for execution on a Dispatcher.
    3. Dispatcher eventually delegates the execution to some ExecutorService.
    4. Actual implementations of Dispatcher and ExecutorService determine the way JVM threads are created and used. By default a fork-join pool-based executor service is used. It is created with ForkJoinExecutorConfigurator which creates a factory which, in turn, creates corresponding implementation of ExecutorService.
    5. Fork-join pool creates several Threads internally and uses them to execute given tasks. The actual execution strategy is unimportant; there are other executors which can schedule tasks across threads differently, and Akka can be configured to use them instead of the default one.
    6. Thread is a JVM-level abstraction which is implemented in the standard library. It uses bindings to native libraries (written in some native language like C++ or C) which delegate thread management to the OS.
    7. So, when Thread object is created and started, eventually a native thread is created and started, and this thread will execute the code provided to this Thread object.

    Here is a simple diagram of how actors are executed:

    Actor -> Dispatcher -> ExecutorService ----> Thread --|                    |--> OS thread
                                            |--> Thread --|native code boundary|--> OS thread
                                            \--> Thread --|                    |--> OS thread
    

    You can see that there are several layers of abstraction here. The most important one is ExecutorService: it completely defines the actual strategy for instantiation of threads and execution of tasks on these threads. It is possible to write a single-threaded executor service (in fact, there is one in the standard library) which will never spawn additional threads, and it is possible to force Akka to use it.

    The intermediate representation (I presume you mean Java byte code) of actors is not optimized directly into a code which works with native threads because they are completely different levels of abstractions: actors are very high-level and provide numerous guarantees about execution order, while threads are very low-level and have to be used with extensive care if you want the program to be correct. Akka ensures that threads are used correctly, so you don't have to think about it yourself.