引言
在Akka中, 一个Future
是用来获取某个并发操作的结果的数据结构。这个操作通常是由Actor执行或由Dispatcher直接执行的. 这个结果可以以同步(阻塞)或异步(非阻塞)的方式访问。
Future提供了一种简单的方式来执行并行算法。
Future直接使用
Future中的一个常见用例是在不需要使用Actor的情况下并发地执行计算。
Future有两种使用方式:
- 阻塞方式(Blocking):该方式下,父actor或主程序停止执行知道所有future完成各自任务。通过
scala.concurrent.Await
使用。- 非阻塞方式(Non-Blocking),也称为回调方式(Callback):父actor或主程序在执行期间启动future,future任务和父actor并行执行,当每个future完成任务,将通知父actor。通过
onComplete
、onSuccess
、onFailure
方式使用。
执行上下文(ExecutionContext)
为了运行回调和操作,Futures需要有一个ExecutionContext
。
如果你在作用域内有一个ActorSystem
,它会它自己派发器用作ExecutionContext,你也可以用ExecutionContext伴生对象提供的工厂方法来将Executors和ExecutorServices进行包裹,或者甚至创建自己的实例。
通过导入ExecutionContext.Implicits.global
来导入默认的全局执行上下文。你可以把该执行上下文看做是一个线程池,ExecutionContext是在某个线程池执行任务的抽象。
如果在代码中没有导入该执行上下文,代码将无法编译。
阻塞方式
第一个例子展示如何创建一个future,然后通过阻塞方式等待其计算结果。虽然阻塞方式不是一个很好的用法,但是可以说明问题。
这个例子中,通过在未来某个时间计算1+1,当计算结果后再返回。
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
object FutureBlockDemo extends App{
implicit val baseTime = System.currentTimeMillis
// create a Future
val f = Future {
Thread.sleep(500)
1+1
}
// this is blocking(blocking is bad)
val result = Await.result(f, 1 second)
// 如果Future没有在Await规定的时间里返回,
// 将抛出java.util.concurrent.TimeoutException
println(result)
Thread.sleep(1000)
}
代码解释:
- 在上面的代码中,被传递给Future的代码块会被缺省的
Dispatcher
所执行,代码块的返回结果会被用来完成Future
。 与从Actor返回的Future不同,这个Future拥有正确的类型, 我们还避免了管理Actor的开销。Await.result
方