STM 软件事务内存——本质是为提高并发,通过事务来管理内存的读写访问以避免锁的使用

对Java程序员来说,我们对面向对象的编程(OOP)自然都是烂熟于胸的,但语言也极大地影响了我们构建面向对象应用程序的方式。(现在的OOP已经和Alan Kay当初创造这个词时候的初衷大不相同了,他的主要思想是采用消息传递并消灭所有状态数据(他认为,系统是由一些类似于生物细胞那样的对象构成的,这些对象通过消息传递进行通信,且无需持有任何状态)——go语言)

对于Java程序员来说,当我们顺着指针或引用找到某个实例的时候,实际上是登录到了持有其状态的一块内存上,于是在那个位置上操纵数据也就成了自然而然的事了。该位置即代表了对象实例及其所包含的数据。将实体与状态进行合并最初看起来是非常简单且易于理解的,但从并发的角度来看,这种做法其实有很多严重的不良后果。例如,如果我们需要实现一个打印银行账户详情(资金数量、当前余额、交易信息、最小余额等等)的程序,我们就会碰到很多并发相关的问题。你会发现手头待处理的引用其实是一个随时都可能发生变化的状态的代理。所以当我们查看账户信息的时候,就需要通过加锁来阻止其他线程对账户内容进行修改,而这也必将导致并发度的大幅下降。但问题并不是从加锁的那一刻才开始出现的,而是在我们把账户的实体与其状态合并的时候就已经存在了。

我们曾经被告知说面向对象的编程是对真实世界的建模。但悲催的是,真实世界与OO范式所试图构建的模型实际是大相径庭的。因为在真实的世界中,状态是不变的,而实体却是不断变化的。接下来我们将讨论这种说法为何是正确的。

将实体与状态分离

你能快速告诉我Google的股价现在是多少吗?我们当然可以说从证券市场开市的那一刻起股价就是在不断变化的。举一个简单的例子,2010年12月10日Google的收盘价是592美元,并且这个数字已经被载入史册、是不会再改变了。当然,Google今天的股价和那天已经完全不同了。而如果过几分钟之后再来查看Google的股价(假设证券市场是开市的),我们就会看到一个不一样的值,但之前的那个值其实并没有改变。从现在开始,我们得改变一下我们对对象的认识,而这也将同时改变我们使用对象的方式。后面我们会看到,把对象的实体与其不可变状态值进行分离的做法将如何帮助我们实现锁无关编程、提高并发度、同时最大程度地降低竞争。

将实体与状态分离绝对是一个天才的构想,这是STM模型过程中所采用的一个非常关键的步骤。假定我们的Google股票对象由两部分组成:第一部分用于表示该股的实体,其中包含一个指向第二部分的指针。第二部分则包含了该股最新股价,其中保存股价的变量即为不可变状态,如图 1所示。

 

图 1 将可变实体部分与不可变状态值进行分离

一旦接收到一个新的股价信息,我们就可以将其放入历史价格指数中。由于旧的股价是不可变的,所以我们可以将其共享出去供所有线程访问。Google股票对象就可以多快好省地对外提供数据读取服务。而一旦有新的数据准备就绪之后,我们可以快速更改实体中的指针,以使其指向保存新股价的字段。实体与状态分离的做法对于并发来说也是一大福音。因为采用了这种方法之后,我们就可以不用阻塞任何查询股价的请求了。由于状态是不会变的,所以我们可以欣然将其指针传递给发出查询请求的线程。

STM中的事务

事务的概念源自于数据库管理系统(DBMS)中数据库事务的概念。在数据库管理系统中,事务必须满足ACID性质,即原子性,一致性,隔离性和持久性。原子性指的是事务中的动作要么全部执行,要么一个都不执行;一致性指的是任何时刻,数据库必须处于一致性状态,即必须满足某些预先设定的条件;隔离性是指一个事务不能看见其他未提交事务所涉及到的内部对象的状态,而持久性则是指一个已提交的事务对数据库系统的改变必须是永久的。由于STM中的数据是全都放在内存而不是数据库或文件系统里的,所以STM只提供了事务的前三个属性,而缺少了对持久性的支持。通过将对内存的访问封装在事务(transactions)中,STM消除了多线程内存同步过程中我们易犯的那些错误!

在Clojure语言中,STM实现采用了与数据库相似的多版本并发控制技术(MVCC),其并发控制也和数据库中的乐观锁(optimistic locking)很像。当我们启动一个事务的时候,STM会记录一下时间戳,并将事务中将会用到所有ref都拷贝一份。由于状态是不可变的,所以对于ref的拷贝是多快好省的。当对某个不可变状态进行“变更”的时候,我们其实并没有改变它的值(value),而是为其创建了一个含有新值的拷贝。该拷贝是本事务的一个内部状态,并且由于我们使用了持久化的数据结构,这一步也是多快好省的。而如果STM识别出我们操作过的ref已经被别的事务改了的话,它就会中止并重做本事务。当事务成功完成时,所有的变更都会被写入内存,而时间戳也将被更新!

STM中的事务实现

软件事务内存的实现包括原子对象(Atomic object)、冲突判决器(Conflict manager)。其中原子对象的实现是最重要的,它是各事务之间通信同步的媒介。原子对象的实现又分为顺序性实现和事务实现:其中事务实现还要要求实现同步和恢复(recovery)功能,同步功能即意味着要求有检测事务冲突的能力,而恢复功能则意味着需要在事务失败的时候将对象回滚到事务执行之前的状态。目前提出的原子对象一般是基于读/写冲突(Read/Write conflict)的机制:原子对象提供两个接口,一个为读接口,一个为写接口,通过读接口可以得到一个可以读的对象,而通过写接口则可以得到一个可以写的对象。为了检测冲突(即多个事务并发时的同步情况),事务中可以设立两个集合,一个为读集(Read set),一个为写集(Write set),分别记录该事务所要处理的读写原子对象集。如果一个事务的读集或写集与另一个事务的写集有交叉,则表明两个事务冲突,需要冲突判决器进一步采取决策。

总结:STM 软件事务内存——本质是为提高并发,通过事务来管理内存的读写访问以避免锁的使用!对于clojure,akka来说,只需将对事务内存的操作封装为事务,简化了并发编程而让程序员无需考虑复杂的事务同步问题!

时间: 2024-10-21 01:43:08

STM 软件事务内存——本质是为提高并发,通过事务来管理内存的读写访问以避免锁的使用的相关文章

理解Clojure STM 软件事务性内存

翻译说明: 英文原文来自:http://java.ociweb.com/mark/stm/article.html 原文包含了一些非STM的知识,也包括STM底层实现的内容,这里只是翻译了STM抽象层的内容,自认为这部分比较重要. 翻译是基于自己能够理解的方式翻译的,并非逐句翻译,目的是理解STM,理解如何调优STM,有逐句翻译强迫症的同学请不要喷我! 本人是在学习<Clojure编程乐趣>的"压力之下的 Ref"章节,遇到无法理解minHistory和maxHistory

3.软件开发的本质和基本手段

3.1 软件开发的含义 正确认识软件开发,是从事软件开发的思想基础. 软件开发的本质: 不同抽象层术语之间的“映射” 不同抽象层处理逻辑之间的“映射” 3.2 实现映射的基本手段:建模 建模:是解决问题的一般途径! 具体地说:模型是特定意图下所确定的角度和抽象层次上对物理系统的描述,通常包含对系统边界的描述,给出系统内各模型元素记忆它们之间的语义关系. 原文地址:https://www.cnblogs.com/jasonwu/p/10735396.html

Android最佳性能实践(一)——合理管理内存

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/42238627 内存(RAM)对于任何一个软件开发环境都是种非常珍贵的资源,而对于移动操作系统来讲的话,则会显得更加珍贵,因为手机的硬件条件相对于PC毕竟是比较落后的.尽管Android系统的虚拟机拥有自动回收垃圾的机制,但这并不代表我们就可以忽视应该在什么时候分配和释放内存. 为了使垃圾回收器可以正常释放程序所占用的内存,在编写代码的时候就一定要注意尽量避免出现内存泄漏的情况(通

Android,合理管理内存

[-] 节制地使用Service 当界面不可见时释放内存 当内存紧张时释放内存 避免在Bitmap上浪费内存 使用优化过的数据集合 知晓内存的开支情况 谨慎使用抽象编程 尽量避免使用依赖注入框架 使用ProGuard简化代码 使用多个进程 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/42238627 有不少朋友都问过我,怎样才能写出高性能的应用程序,如何避免程序出现OOM,或者当程序内存占用过高的时候该怎么样去排查.确实,一个

内存管理 &amp; 内存优化技巧 浅析

内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户体验.如何让回收那些不再使用的对象呢?本文着重介绍OC中的内存管理. 所谓内存管理,就是对内存进行管理,涉及的操作有: 1.分配内存:比如创建一个对象,会增加内存占用: 2.清除内存:比如销毁一个对象,会减少内存占用. 内存管理的管理范围: 1.任何继承了NSObject的对象: 2.对其他非对象类

SQL Server数据库读写分离提高并发性

在一些大型的网站或者应用中,单台的SQL Server 服务器可能难以支撑非常大的访问压力.很多人在这时候,第一个想到的就是一个解决性能问题的利器——负载均衡.遗憾的是,SQL Server 的所有版本,包括2012年3月发布的SQL Server 2012,也未提供该功能. 扩展单台SQL Server 服务器,解决性能瓶颈,有两种方法: 一.分布式数据库.扩展和分布数据库到多台服务器,由多台服务器分布存储不同的数据,通过将数据和访问压力分布到多台服务器来解决性能瓶颈.以一个大型电子商务网站数

Java是如何管理内存的?

本文转自CSDN用户Kevin涂腾飞的文章java内存管理机制:http://blog.csdn.net/tutngfei1129287460/article/details/7383480 JAVA 内存管理总结 1. java是如何管理内存的 Java的内存管理就是对象的分配和释放问题.(两部分) 分配 :内存的分配是由程序完成的,程序员需要通过关键字new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间. 释放 :对象的释放是由垃圾回收机制决定和执行的,

内存管理 浅析 内存管理/内存优化技巧

内存管理 浅析 下列行为都会增加一个app的内存占用: 1.创建一个OC对象: 2.定义一个变量: 3.调用一个函数或者方法. 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户体验.如何让回收那些不再使用的对象呢?本文着重介绍OC中的内存管理. 所谓内存管理,就是对内存进行管理,涉及的操作有: 1.分配内存:比如创建一个对象,会增加内存占用: 2.清除内存:比如销毁一个对象,会减少内存占用. 内存管理的管理范围: 1.任何继承了NSObject的对象: 2.对其他非对象类

Code First开发系列之管理并发和事务(转)

转自:http://www.cnblogs.com/farb/p/ConcurrencyAndTransctionManagement.html 返回<8天掌握EF的Code First开发>总目录 本篇目录 理解并发 理解积极并发 理解消极并发 使用EF实现积极并发 EF的默认并发 设计处理字段级别的并发应用 实现RowVersion 理解事务 创建测试环境 EF的默认事务处理 使用TransactionScope处理事务 使用EF6管理事务 使用已存在的事务 选择合适的事务管理 本章小结