讨喜的隔离可变性(一)用角色实现隔离可变性

简介:

Java将OOP变成了可变性驱动(mutability-driven)的开发模式[1],而函数式编程则着重强调不可变性,而这两种极端的方式其实都是有问题的。如果每样事物都是可变的,那么我们就需要妥善处理可见性和竞争条件。而在一个真实的应用程序中,也并非所有事物都是不可变的。即使是纯函数式语言也提供了代码限制区,在该区域内允许出现带副作用的逻辑以及按顺序执行这些逻辑的方法。但无论我们倾向于哪种编程模型,避免共享可变性都是毋庸置疑的。

共享可变性——并发问题的根源所在——是指多个线程可以同时更改相同的变量。而隔离可变性——一个可以消除大部分并发问题的不错的折衷方案——是指任意时刻有且只有一个线程(或角色)可以访问某个可变变量。

在OOP中,由于对象的状态都被封装在对象中,所以只有实例函数才能够操作对象的状态。然而不同的线程可以同时调用这些函数,从而导致并发问题的发生。在基于角色(actor)的编程模型中,我们只允许一个角色(actor)操作对象的状态。因此,即使整个应用程序是多线程的,那些角色(actor)本身也都是单线程的,所以也就不会有任何可见性和竞争条件相关的问题。虽然角色(actor)都可以发出操作执行请求,但它们却无法接触到其他角色(actor)托管的可变状态。

我们在使用基于角色(actor)的模型进行编程时,往往会采取与普通的面向对象编程不同的设计方法。即将问题拆分成若干个可计算的异步任务,并将它们赋予不同的角色(actor)。其中,每个角色(actor)只负责执行分配给自己的那部分任务。这样我们就可以将任意可变状态限定在至多一个角色(actor)当中(见图 8‑1)。此外,我们还需要保证在角色(actor)之间传递的消息都是完全不可变的才行。

在这种设计方法中,我们令每个actor都负责解决问题的一个组成部分,而它们所接收的数据则都当作不可变对象来处理。一旦完成了分配给自己的任务,这些角色(actor)就会把处理结果封装到不可变对象中并返回给调用角色或其他特定的后置处理(post-processing)角色。我们可以将这种方式想象为OOP进化版,即我们让可变且活跃的对象分别运行在属于自己的线程里。在这种模式下,对对象进行操作的唯一方法是将消息传递给它们而不是直接调用其成员函数。



[1] 在这个问题上Java还有其他同案犯,所以我们不应该把所有责任都归咎于Java。

目录
相关文章
|
3月前
|
监控 安全 网络安全
内部隔离
【8月更文挑战第18天】
47 5
|
3月前
|
人工智能 达摩院 自然语言处理
类与类之间的协作模式问题之策略模式影响我们在工作中决策过程的问题如何解决
类与类之间的协作模式问题之策略模式影响我们在工作中决策过程的问题如何解决
|
3月前
|
缓存 项目管理
类与类之间的协作模式问题之享元模式在工作中应用的问题如何解决
类与类之间的协作模式问题之享元模式在工作中应用的问题如何解决
|
消息中间件 负载均衡 应用服务中间件
如何避免相互依赖的系统间耦合
如何避免相互依赖的系统间耦合
179 0
如何避免相互依赖的系统间耦合
|
数据格式 XML 数据安全/隐私保护
分布式系统的那些事儿(三) - 系统与系统之间的调用
系统与系统之间的调用通俗来讲,分为本地同一台服务器上的服务相互调用与远程服务调用,这个都可以称之为RPC通信。浅白点讲,客户访问服务器A,此时服务器要完成某个动作必须访问服务器B,服务器A与B互相通信,相互调用,A访问B的时候,A挂起,等待B的响应,B响应返回相应的数据个A,A再返回给用户,这就是一个很简单的栗子。
889 0
|
Java 测试技术 Scala
讨喜的隔离可变性(八)类型化角色和Murmurs
声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。 使用了类型化角色的EnergySource使我们能够以调用函数的形式来掩盖后台顺序处理异步消息的过程,在实现了线程安全的同时又可以免去显式同步的困扰。
1921 0
|
Java Scala
讨喜的隔离可变性(十)使用Transactor
声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。 Akka transactor或事务角色为我们提供了一种将多个角色的执行过程合并到一个事务中的方法。
1671 0