曾有个的医嘱是这样说的:“如果它伤到了你,那就别再用它了”。在并发编程领域,共享可变性就是那个“它”。
虽然JDK的线程API使我们可以非常容易地创建线程,但如何防止线程冲突和逻辑混乱却又成了大问题。STM虽然可以解决部分问题,但是在一些类似Java这样的语言中,我们仍不得不非常小心谨慎地避免非托管可变变量和事务逻辑中产生某些副作用。而令人惊讶的是,当共享可变性消失的时候,所有那些令人纠结的问题也都随之消失了。
事实证明,在相同数据集上起多个线程互相冲突地执行是行不通的。幸运的是我们有更好的办法——基于事件的消息传递。通过这种方法,我们可以将任务当成是应用程序/JVM内部的轻量进程来对待。同时,我们将不可变消息传递给各个任务,从而避免每个任务都去抢占共享数据。一旦这些异步任务执行完毕,它们会将不可变的执行结果返回给另外的协调线程。在下面的章节中我们将学习如何设计这种带有“用于异步交换不可变消息的协调角色(actor)[1]”的应用程序。
虽然这种方法已经问世多年了,但在JVM领域却还是相对较新的技术。基于角色(actor)的模型在Erlang中是非常成功的(参见《Programming Erlang: Software for a Concurrent World》[Arm07]以及《Concnrrent Programming in Erlang》[VWWA96])。而当Scala 2003年被引入时,Erlang的基于角色(actor)的模型也同时被采纳并引入到JVM大家庭中(参见《Programming in Scala》[OSV08]以及《Programming Scala》[Sub09])。
在Java中,有大把基于角色(actor)的并发库[2]可供我们选择:ActorFoundary、Actorom、Actors Guild、Akka、FunctionalJava、Kilim、Jetlang等等。其中部分类库是以面向切面(AspectOriented)的字节码形式来进行组织的。同时,这些类库的成熟度和被大家的接受度也都不尽相同。
在本章中,我们将学习如何编写基于角色(actor)的并发程序。在大多数情况下,我们将使用Akka以便能够直奔核心概念。Akka是一个基于Scala的高性能解决方案,同时还提供了相当好用的Java API。我们既可以将其用于基于角色(actor)的并发,也可以将之用于STM(参见第6章)。