Причина того, что ваш код работает, заключается в том, что оба фьючерса будут выполняться одним и тем же потоком.Создаваемый вами ExecutionContext
не будет использовать Thread
напрямую для каждого Future
, а вместо этого будет планировать выполнение задач (Runnable
экземпляров).Если в пуле больше нет доступных потоков, эти задачи будут помещены в BlockingQueue
в ожидании выполнения.(Подробнее см. API ThreadPoolExecutor )
Если вы посмотрите на реализацию Executors.newFixedThreadPool(1)
, вы увидите, что создается Исполнитель с неограниченной очередью:
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue[Runnable])
Чтобы получить эффект истощения потока, который вы искали, вы можете самостоятельно создать исполнителя с ограниченной очередью:
implicit val ec = ExecutionContext.fromExecutor(new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue[Runnable](1)))
Поскольку минимальная емкость ArrayBlockingQueue
равна 1, вам потребуется три фьючерса.чтобы достичь предела, и вам также необходимо добавить некоторый код, который будет выполняться в результате будущего, чтобы не допустить их завершения (в приведенном ниже примере я делаю это путем добавления .map(identity)
)
.В следующем примере
import scala.concurrent._
implicit val ec = ExecutionContext.fromExecutor(new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue[Runnable](1)))
def addOne(x: Int) = Future {
x + 1
}
def addTwo(x: Int) = Future {
addOne(x + 1) .map(identity)
}
def addThree(x: Int) = Future {
addTwo(x + 1).map(identity)
}
println(addThree(1))
завершается с
java.util.concurrent.RejectedExecutionException: Task scala.concurrent.impl.CallbackRunnable@65a264b6 rejected from java.util.concurrent.ThreadPoolExecutor@10d078f4[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 1]