scalaakka

Creating a custom thread inside an actor?


When my akka based aplication runs, sometimes I see this error:

[ERROR] [2024-01-02 07:30:41.524] [xxxx.dispatcher-22680] Uncaught error from thread [xxxx.dispatcher-22680]: unable to create new native thread, shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[....]
java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:719)
    ....

It happens when a remote service is unavailable.

Mapping the error logs to code, I see that inside an actor a thread is being created:

val refreshThread = new Thread() {....}
refreshThread.start()

This is where error happens.

I understand that an akka actor is executed on a thread whose scheduling and all other management is taken care by akka dispatcher, thus freeing developers from getting into thread related code. But here a thread is created inside actor itself.

How are such threads managed by akka?

I want to know whether creating a custom thread like this is an acceptable practise in context of akka.

Also, what is correct way of handling this scenario?


Solution

  • Such threads are not going to be managed by Akka at all, as is generally the case for any resource within an actor.

    Is this an acceptable practice in Akka? Almost certainly not: there are generally better abstractions available. Which abstraction to choose depends on what exactly you want the thread to do.

    For instance, a Thread is a Runnable: it only forks off as a new thread if start() is called. You can instead obtain a dispatcher (a thread pool) from the ActorSystem and have that dispatcher run the Runnable on one of its threads rather than on a new thread: the exact mechanism for this will differ slightly between Akka Classic and Akka Typed. Note that if going down this path, the Thread should not be creating new threads (as then we're basically back here) nor should the task being executed be long-running. You may find it useful to configure a dispatcher (e.g. one backed by a thread-pool-executor for tasks which block) for the purpose of running threads like this. Note that executing things on a thread pool means that they may not actually execute until some time has passed.

    Another alternative (potentially more ideal), is to refactor what the thread is doing into smaller work units and use an abstraction like futures or actors to manage the work units, but this may be a substantial refactoring.