Scala Execution Context
An ExecutionContext is the worker pool that runs Future jobs. It decides how many jobs can run at the same time.
The global pool runs a Future
The global pool is the shared default. Import it, then a Future runs on it.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
@main def main(): Unit =
val f = Future { 4 * 5 }
val result = Await.result(f, 5.seconds)
println(result)A custom pool can run one job at a time
A single-thread executor makes a pool with one worker. The Future runs on that worker, and the executor is shut down after the wait.
import scala.concurrent.{Future, Await, ExecutionContext}
import scala.concurrent.duration.DurationInt
import java.util.concurrent.Executors
@main def main(): Unit =
val executor = Executors.newSingleThreadExecutor()
given ExecutionContext = ExecutionContext.fromExecutor(executor)
val f = Future { 6 * 7 }
val result = Await.result(f, 5.seconds)
executor.shutdown()
println(result)One custom pool can run two jobs
Both Futures use the same custom pool. Only their awaited sum is printed, so the order the jobs run in does not matter.
import scala.concurrent.{Future, Await, ExecutionContext}
import scala.concurrent.duration.DurationInt
import java.util.concurrent.Executors
@main def main(): Unit =
val executor = Executors.newSingleThreadExecutor()
given ExecutionContext = ExecutionContext.fromExecutor(executor)
val first = Future { 5 }
val second = Future { 10 }
val total = Await.result(first, 5.seconds) + Await.result(second, 5.seconds)
executor.shutdown()
println(total)Example
import scala.concurrent.{Future, Await, ExecutionContext}
import scala.concurrent.duration.DurationInt
import java.util.concurrent.Executors
@main def main(): Unit =
val executor = Executors.newSingleThreadExecutor()
given ExecutionContext = ExecutionContext.fromExecutor(executor)
val first = Future { 12 }
val second = Future { 8 }
val total = Await.result(first, 5.seconds) + Await.result(second, 5.seconds)
executor.shutdown()
println(total)Key points
- ExecutionContext: the worker pool that runs the work inside a Future.
- Global: ExecutionContext.Implicits.global is the shared default pool.
- Import: importing global brings that shared pool into scope.
- Custom: a program can make its own pool from a single-thread executor.
- Shutdown: shut a custom executor down after the work finishes, so the program can end.
In this example, a single-thread executor is turned into a custom pool and given as the context for the Futures. The two Futures hold 12 and 8, their awaited values are added, the executor is shut down, and the total 20 is printed.
Note
the global pool is shared. A custom pool keeps a program's jobs apart from other jobs.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Scastie
Exercise
Why should a custom executor be shut down after the work finishes?