面向对象语言中,对象之间通信,依靠的是对象方法。
Actor模型中,Actor之间通信,依靠的是消息。
Hello Actor模型
Actor模型本质上是一种计算模型,基本的计算单元称为Actor。在Actor模型中,所有的计算都是在Actor中执行的。在面向对象编程里面,一切都是对象;在Actor模型里,一切都是Actor,并且Actor之间是完全隔离的,不会共享任何变量。
并发问题的根源就在于共享变量,而Actor模型中Actor之间不共享变量,那用Actor模型解决并发问题,相当顺手。Actor模型是一种并发计算模型。
Java语言本身并不支持Actor模型,需要借助第三方类库,如Akka。
1 | //该Actor收到消息message后,会打印Hello message |
先创建一个ActorSystem(Actor不能脱离ActorSystem存在);之后创建一个HelloActor,Akka中创建Actor并不是new一个对象出来,而是通过调用system.actorOf()方法创建的,该方法返回的是ActorRef,而不是HelloActor;最后通过调用ActorRef的tell()方法给HelloActor发送了一条消息“Actor”。
Actor模型和面向对象编程契合度非常高,完全可以用Actor类比面向对象编程里面的对象,而且Actor之间的通信方式完美的遵守了消息机制,而不是通过对象方法来实现对象之间的通信。
消息和对象方法的区别
Actor中的消息机制,可以类比现实世界里的写信。Actor内部有一个邮箱(Mailbox),接收到的消息都是先放到邮箱里,如果邮箱里有积压的消息,那么新收到的消息就不会马上得到处理。也因为Actor使用单线程处理消息,所以不会出现并发问题。可以把Actor内部的工作模式想象成只有一个消费者线程的生产者-消费者模式。
在Actor模型里,发送消息仅仅是把消息发出去而已,接收消息的Actor在接收到消息后,也不一定会立即处理。Actor中的消息机制完全是异步的;而调用对象方法,实际上是同步的,对象方法return之前,调用方会一直等待。
调用对象方法,需要持有对象的引用,所有的对象必须在同一个进程中。而在Actor中发送消息,类似于显示中的写信,只需要知道对方的地址就可以,发送消息和接收消息的Actor可以不在一个进程中,也可以不在同一台机器上。因此Actor模型不但适用于并发计算,还适用于分布式计算。
Actor的规范化定义
Actor是一种基础的计算单元,包括三部分能力:
- 处理能力,处理接收到的消息。
- 存储能力,Actor可以存储自己的内部状态,并且内部状态在不同Actor之间是绝对隔离的。
- 通信能力,Actor可以和其他Actor之间通信。
当一个Actor接收到一条消息后,可以做三件事:
- 创建更多的Actor;
- 发消息给其它Actor;
- 确定如何处理下一条消息。
Actor具备存储灵力,它有自己的内部状态,所以可以把Actor看作一个状态机,把Actor处理消息看作是触发状态机的状态变化;而状态机的变化往往要基于上一个状态,触发状态机发生变化的时刻,上一个状态必须是确定的,所以确定如何处理下一条消息,本质上是改变内部状态。
在多线程中,由于可能存在竞态条件,所以根据当前状态确定如何处理下一条消息还是有难度的,需要使用各种同步工具,但在Actor模型里,由于是单线程处理,所以就不存在竞态条件问题。
用ACtor实现累加器
支持并发的累加器是最简单并且有代表性的并发问题,可以基于互斥锁方案实现,可以基于原子类实现,也可以用Actor来实现。
1 | //累加器 |
CounterActor内部持有累计值counter,当CounterActor接收到一个数值型的消息message时,就将累加值counter += message;如果是其它类型的消息,则打印当前累计值counter。在main()方法中,启动了4个线程来执行累加操作。整个程序没有锁,也没有CAS,但是程序是线程安全的。
总结
Actor模型是一种简单的计算模型,Actor是最基本的计算单元,Actor之间是通过消息进行通信。Actor与面向对象编程(Object Oriented Programming,OOP)中的对象匹配度高。在面向对象编程里,系统由类似于生物细胞那样的对象构成,对象之间也是通过消息进行通信,所以在面向对象语言里使用Actor模型理所当然。
在Java领域,可以使用Akka来支持Actor模型,也可以使用Vert.x,相对于Akka的显示实现,Vert.x是Actor模型的隐式实现。
Actor可以创建新的Actor,这些Actor最终会呈现出一个树状结构,类似现实世界里的组织结构。Actor模型和现实世界一样都是异步模型,理论上不保证消息百分百送达,也不保证消息送达的顺序和发送的顺序一致,更无法保证消息会被百分百处理。这些都是使用Actor模型的成本。