What is an ExecutorService?

It is an interface that provides abstraction over creating, managing and scheduling tasks on threads. We only have to use this to execute tasks asynchronously.

Creating an ExecutorService

Most of the times, factory methods provided factory Executors are enough for the use cases. However, we can also create ExecutorService directly using ThreadPoolExecutor.

val executorService: ExecutorService = ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())

Creating by using factory methods,

val exService: ExecutorService = Executors.newFixedThreadPool(10)
val singleExService: ExecutorService: Executors.newSingleThreadExecutor()

Schedule a task

ExecutorService provides methods such as

  1. execute
  2. submit
  3. invokeAny and
  4. invokeAll
executorService.execute(runnable)
 
val future: Future[Unit] = executorService.submit(runnable)

Shutting down

ExecutorService won’t get shutdown automatically. It keeps waiting for new tasks to assign. We have to manually shut it down.

We can do so by using methods,

  1. shutdown
  2. shutdownNow

Using ExecutorService

In scala, we can either directly schedule tasks on executorService or provide this to an ExecutionContext.

With the previous approach, we have to manually convert java.Future to scala.Future. In latter approach, ExecutionContext does everything by itself.

val ec: ExecutionContext = ExecutionContext.fromExecutor(exService)
 
val f: Future[Int] = Future {
    printWithThread("From future")
    1
}(using ec)

References

  1. A guide to the java ExecutorService