[英]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))))
令我惊讶的是,它有效。 我不知道为什么。
题:
为什么给定一个线程可以同时执行两个期货?
我的期望 :
第一个Future
( addTwo
)占用唯一的线程( newFixedThreadPool(1)
),然后调用另一个Future
( addOne
),它再次需要另一个线程。
所以该程序应该最终缺乏线程并陷入困境。
你的代码工作的原因是两个期货都将由同一个线程执行。 您正在创建的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.