Actor模型(并发计算模型)
致力于解决:
(1) 多线程行为, 我们可以编写高并发的程序, 而没有必要关注内存可见性
(2) 系统及其组件之间的透明远程通信, 便于维护困难的网络代码
(3) 集群,高可用性架构,具有弹性
Actor 模型
1. 为什么我们要引入这种编程模型
(1) 对封装的挑战, 封装可以保护类的数据不能直接在外部访问, 但是在多线程环境中, 这种封装相对于来说比较脆弱, 我们要解决多个线程访问一个变量的方式那就是通过线程同步(锁)机制来实现. 但是引入锁机制(包括分布式锁), 带来了新的问题:(锁限制了并发性, 调用者线程阻塞带来的浪费, 引入新的问题死锁)
(2) The illusion of shared memory(共享内存的异常), 在现代的计算机中, 我们的写入一般是对于缓存行而不是直接内存, 这就导致了一个内核的写入操作可能不会被另一内核直接看到, 为了保证可见性, 我们需要将缓存行中的数据同步到内存中, 并且要求程序从内存中读取变量而不是缓存, 这就引入了可见性问题. jvm上 volitile标记变量的代价是很高的.
(3) 调用堆栈引发的错误, 这种情况发生在某个调用者线程将某些数据(通过queue)交被调用者线程操作, 然后在某个时刻调用者线程可以获取某个结果. 这种情况会发生一些问题. 例如, 在被调用者执行的时候产生了异常如何通知调用者线程? , 这会产生丢失任务的可能.
2. Actor模型的结构
(1) A mailbox (存储消息的邮箱)
(2) A behavior (Actor的状态, 我理解就是其执行的操作)
(3) Messages (消息, 一种信号, 相当于函数参数, 这个是Actor的参数)
(4) An execution:执行 environment (执行环境, 也就是消息接收到之后进行的某种操作)
(5) An address (在系统结构中的路径)
Actor模型三大要素 消息, 行为, 状态
3. Actor模型特点
(1) 采用消息传递的方式解决死锁和阻塞问题, 也就是通过发送消息实现调用者线程调用工作线程, 也是通过发消息的形式实现返回值. 这样可以解决异常返回的问题. 也就是说Actor之间的通信是安全的.
(2) 恢复封装, 因为每一个Actor对于其他的Actor来说都是封闭的, 他们之间通过message来联系, 每个Actor拥有自己的变量, 也就是说Actor本身是线程安全的.
好处:
-
事件驱动模型 - Actors执行工作以响应消息。Actors之间的通信是异步的,允许Actors发送消息并继续自己的工作而不会阻塞等待回复。
-
强隔离原则 - 与Java中的常规对象不同,Actor在您可以调用的方法方面没有公共API。相反,它的公共API是通过actor处理的消息定义的。这可以防止Actors之间的任何状态共享; 观察另一个演员状态的唯一方法是向它发送一条要求它的消息。
-
位置透明性 - 系统从工厂构造Actors并返回对实例的引用。由于位置无关紧要,因此Actor实例可以启动,停止,移动和重新启动以进行向上和向下扩展以及从意外故障中恢复。
-
轻量级 - 每个实例仅消耗几百个字节,这实际上允许在单个应用程序中存在数百万个并发Actors
4. 响应式编程是什么
引用别人博客上的一句话觉得说的很好:响应式编程(reactive programming)是一种基于数据流(data stream)和变化传递(propagation of change)的声明式(declarative)/异步的编程范式
基于数据流? 基于数据流指的是我们需要操作的所有的event会以数据流的形式发出(event事件流), 需要我们处理的event串起来就像是数据流一样, 我们会对流的每一个事件做相应处理.
变化传递? 对一个状态的变化会引起其他的Actor或者其监听者的状态变化. 比如Actor抛出了某个异常终止了, 这样其子孙也会终止, 并且会给监听他的Actor发送消息.引起监听者的状态也会随之发生变化
声明式编程范式? 类似于我们strem().map() 类似于map方法, 就是我们的处理行为已经声明, 对于数据流只需要根据其特征做相应处理就可以了, 计算逻辑不会改变