🎓 A1Learner
🔍
0 of 45 lessons completed

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?