簡體   English   中英

為什么這個Scala代碼在一個線程中執行兩個Futures?

[英]Why this Scala code execute two Futures in one thread?

我一直在使用多線程,但無法解釋這么簡單的情況。

import java.util.concurrent.Executors
import scala.concurrent._
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1))

def addOne(x: Int) = Future(x + 1)
def addTwo(x: Int) = Future {addOne(x + 1)}

addTwo(1)
// res5: Future[Future[Int]] = Future(Success(Future(Success(3))))

令我驚訝的是,它有效。 我不知道為什么。

題:
為什么給定一個線程可以同時執行兩個期貨?

我的期望
第一個FutureaddTwo )占用唯一的線程( newFixedThreadPool(1) ),然后調用另一個FutureaddOne ),它再次需要另一個線程。
所以該程序應該最終缺乏線程並陷入困境。

你的代碼工作的原因是兩個期貨都將由同一個線程執行。 您正在創建的ExecutionContext將不會直接為每個Future使用Thread ,而是安排要執行的任務( Runnable實例)。 如果池中沒有可用的線程,這些任務將被放入等待執行的BlockingQueue (有關詳細信息,請參閱ThreadPoolExecutor API

如果查看Executors.newFixedThreadPool(1)的實現,您將看到創建一個具有無界隊列的Executor:

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]

將它擴展到Promise很容易理解

val p1 = Promise[Future[Int]]
ec.execute(() => {
  // the fist task is start run
  val p2 = Promise[Int]
  //the second task is submit , but no run
  ec.execute(() => {
    p2.complete(Success(1))
    println(s"task 2 -> p1:${p1},p2:${p2}")
  })
  //here the p1 is completed, not wait p2.future finish
  p1.complete(Success(p2.future))
  println(s"task 1 -> p1:${p1},p2:${p2}")// you can see the p1 is completed but the p2 have not
  //first task is finish, will run second task
})
val result: Future[Future[Int]] = p1.future

Thread.sleep(1000)
println(result)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM