Thread Pools
A thread pool is a fixed group of worker threads that share tasks. Tasks wait in a queue when every worker is busy.
A fixed pool runs the tasks
Four tasks square their numbers. Sequence collects the squares in submission order.
import scala.concurrent.{Future, Await, ExecutionContext}
import java.util.concurrent.Executors
import scala.concurrent.duration.*
@main def main(): Unit =
val pool = Executors.newFixedThreadPool(2)
val ec = ExecutionContext.fromExecutorService(pool)
given ExecutionContext = ec
val futures = List(1, 2, 3, 4).map(i => Future(i * i))
val result = Await.result(Future.sequence(futures), 5.seconds)
println(result)
ec.shutdown()A pool adds ten to each number
Three tasks run on three workers. The results stay in submission order.
import scala.concurrent.{Future, Await, ExecutionContext}
import java.util.concurrent.Executors
import scala.concurrent.duration.*
@main def main(): Unit =
val pool = Executors.newFixedThreadPool(3)
val ec = ExecutionContext.fromExecutorService(pool)
given ExecutionContext = ec
val futures = List(1, 2, 3).map(i => Future(i + 10))
val result = Await.result(Future.sequence(futures), 5.seconds)
println(result)
ec.shutdown()A pool can traverse inputs
Each input is multiplied by five on the pool. The results stay in input order.
import scala.concurrent.{Future, Await, ExecutionContext}
import java.util.concurrent.Executors
import scala.concurrent.duration.*
@main def main(): Unit =
val pool = Executors.newFixedThreadPool(2)
val ec = ExecutionContext.fromExecutorService(pool)
given ExecutionContext = ec
val all = Future.traverse(List(2, 3))(x => Future(x * 5))
println(Await.result(all, 5.seconds))
ec.shutdown()Example
import scala.concurrent.{Future, Await, ExecutionContext}
import java.util.concurrent.Executors
import scala.concurrent.duration.*
@main def main(): Unit =
val pool = Executors.newFixedThreadPool(3)
val ec = ExecutionContext.fromExecutorService(pool)
given ExecutionContext = ec
val futures = List(1, 2, 3, 4, 5, 6).map(i => Future(i * 2))
val result = Await.result(Future.sequence(futures), 5.seconds)
println(result)
println(result.sum)
ec.shutdown()Key points
- Worker: A worker is a thread that runs one task at a time.
- Use ExecutionContext.fromExecutorService to turn a Java executor into a Scala execution context.
- Fixed pool: A fixed pool keeps the same number of workers from start to finish.
- Use shutdown to stop a pool after its tasks finish, so the program can end cleanly.
- Order: Collecting futures with Future.sequence keeps results in submission order even when tasks finish out of order.
In this example, a pool of three workers doubles each number from 1 to 6. Sequence collects the doubled values in order, and the program prints the list and then their sum.
Note
In Scala, a pool with too few workers can stall when tasks wait for each other.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Scastie
Exercise
What does shutdown do to a thread pool?