同步异步,阻塞非阻塞,在IO模型中几个概念组合在一起不是很容易理解,但是只从代码执行的角度看同步异步是很清晰的:
同步代表这段代码中的逻辑必须执行完毕,而异步代表调用马上返回,但通常情况下是获取不到需要的值。
同步:val value={
Thread.sleep(2000)
1
}
异步: val value=Future{
Thread.sleep(2000)
1
}
在scala repl执行上面代码可以发现同步等待两秒后返回结果 Int 1,而异步马上返回了
value: scala.concurrent.Future[Int] = List()。
注意:在shell中必须导入 (1)import scala.concurrent.Future 以及 (2) import scala.concurrent.ExecutionContext.Implicits.global
导入(1)是因为需要使用Future,(实际上使用了Future.apply方法),导入(2)则是由异步编程的内在逻辑决定的。
同步方法中的逻辑是由main主线程逐步执行的,而异步编程的思路是:
在执行Future.apply{异步代码块}时,主线程将异步代码块交给新的线程,新起的线程负责异步代码块的计算,而主线程则解放出来,执行下一步。
在scala的Future中,apply方法如下:
def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T]
我们导入(2) import scala.concurrent.ExecutionContext.Implicits.global,实际上这是scala提供的默认的work-stealing thread pool,我们也可以explicitly地申明:
import scala.concurrent.ExecutionContext
implicit lazy val workStealingPoolExecutionContext: ExecutionContext = {
val workStealingPool :ExecutorService = Executors.newWorkStealingPool
ExecutionContext.fromExecutor(workStealingPool)
}
将implicit 的 ExecutionContext 传给了Future中的(implicit executor: ExecutionContext),使用lazy是为了节约资源,即使我们实现了ExecutionContext ,但实际上不会马上申请线程占用资源,只有真正调用Future方法时,才会执行lazy后面的代码。
在获取future值得时候,我们可以用阻塞的方式,也可以使用回调。
阻塞:
import scala.concurrent.duration._
import scala.concurrent.Await
val resultValue= Await.result(value, 2 seconds)
result的方法如下
def result[T](awaitable: Awaitable[T], atMost: Duration): T =
blocking(awaitable.result(atMost)(AwaitPermission))
}
传入最大等待时长,在这期间阻塞获取future的值,超时后报错,通常用于测试用例。
回调:
value onComplete {
case Success(intValue) => println("success: "+intValue)
case Failure(error) => println("An error has occured: " + error.getMessage)
}
可以通过模式匹配的方式获取值。
实际上java 在jdk1.5后增加了callable,也实现了Future,但是java的Future只能通过阻塞等待结果,而scala中的Oncomplete,OnSuccess等回调能充分地利用多核。
网友评论