blocking keyword in Scala

别说谁变了你拦得住时间么 提交于 2019-12-03 11:46:01
Michael Zajac

blocking acts as a hint to the ExecutionContext that it contains blocking code, so that it may spawn a new thread to prevent deadlocks. This presumes the ExecutionContext can do that, but not all are made to.

Let's look at each one-by-one.


Future(blocking(blockingCall()))

This requires an implicit ExecutionContext to execute the Future. If the ExecutionContext being used is a BlockContext (like scala.concurrent.ExecutionContext.Implicits.global is), it may be able to spawn a new thread in its thread pool to handle the blocking call, if it needs to. If it isn't, then nothing special happens.


blocking(Future(blockingCall()))

This tells us that Future(blockingCall()) may be a blocking call, so it is treated the same as above. Except here, Future.apply is non-blocking, so using blocking effectively does nothing but add a little overhead. It doesn't matter what ExecutionContext we're calling it from here, as it isn't blocking anyway. However, the blocking call within the Future will block a thread in the ExecutionContext it's running on, without the hint that its blocking. So, there is no reason to ever do this.

I've explained blocking more in depth in this answer.


REPL Examples:

import java.util.concurrent.Executors
import scala.concurrent._
val ec = scala.concurrent.ExecutionContext.Implicits.global
val executorService = Executors.newFixedThreadPool(4)
val ec2 = ExecutionContext.fromExecutorService(executorService)

def blockingCall(i: Int): Unit = { Thread.sleep(1000); println("blocking call.. " + i) }

// Spawns enough new threads in `ec` to handle the 100 blocking calls
(0 to 100) foreach { i => Future(blocking(blockingCall(i)))(ec) }

// Does not spawn new threads, and `ec2` reaches thread starvation
// execution will be staggered as threads are freed
(0 to 100) foreach { i => Future(blocking(blockingCall(i)))(ec2) }

// `blocking` does nothing because the `Future` is executed in a different context,
// and `ec2` reaches thread starvation
(0 to 100) foreach { i => blocking(Future(blockingCall(i))(ec2)) }

// `blocking` still does nothing, but `ec` does not know to spawn new threads (even though it could)
// so we reach thread starvation again
(0 to 100) foreach { i => blocking(Future(blockingCall(i))(ec)) }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!