文档章节

程序中的并发分析及处理

猿神出窍
 猿神出窍
发布于 2017/05/26 16:51
字数 2789
阅读 700
收藏 33

引言 

  Web应用中并发控制的特殊性

  上述问题在C/S构架中可以通过长事务来实现,但Web应用是基于Internet网络环境的,其中的并发控制有其内在的特殊性:

  1. Web所基于的网络协议HTTP(Hyper Text Transfer Protocol)是一种无连接的协议,数据库服务器无法保存事务的状态信息;

  2. 用户可以随时中止或启动浏览器中当前主页上的事务。

  由于上述特殊性,Web应用中并发控制不能采用严格的长事务来实现,但可以长事务的思路来实现,在数据读取的时候把相应的数据锁定,在更新阶段把锁放开,然后更新数据。

  Web应用中并发控制的实现

  业务逻辑的实现过程中,往往需要保证数据访问的排他性。如在 金融 系统的日终结算处理中,我们希望针对某个cut-off时间点的数据进行处理,而不希望在结算进行过程中(可能是几秒种,也可能是几个小时),数据再发生 变化。此时,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,就是所谓的“锁”,即给选定的目标数据上锁,使其无法被 其他程序修改。有两种锁机制:即通常所说的“乐观锁(Optimistic Locking)” 和“悲观锁(Pessimistic Locking)”。

  1.乐观锁(Optimistic Locking)

  乐观锁(optimistic locking)则乐观的认为资料的存取很少发生同时存取的问题,因而不作数据库层次上的锁定,为了维护正确的数据,乐观锁定使用应用程序上的逻辑实现版本控制来解决。

  并发控制时,数据不一致的情况一旦发生,有几个解决的 方法 ,一种是先更新为主,一种是后更新的为主,比较复杂的就是检查发生变动的数据来实现,或是检查所有属性来实现乐观锁定。

  Hibernate通过版本号检查来实现后更新为主,这也是Hibernate所推荐的方式,在数据库中加入一个VERSON栏记录,在读取数 据时连同版本号一同读取,并在更新数据时递增版本号,然后比对版本号与数据库中的版本号,如果大于数据库中的版本号则予以更新,否则就回报错误。

  以Hibernate实现版本号控制锁定的话,我们的对象中增加一个version属性,例如:

public class MyAccount {
private int version;
....
public void setVersion(int version) {
this.version = version;
}
public int getVersion() {
return version;
}
....
}

  而在映像文件中,我们使用optimistic-lock属性设定version控制,属性栏之后增加一个标签,例如:

optimistic-lock="version"

  设定好版本控制之后,在上例中如果B客户试图更新数据,将会引发StableObjectStateException例外,我们可以捕捉这个 例外,在处理中重新读取数据库中的数据,同时将B客户目前的数据与数据库中的数据读出来,让B客户有机会比对不一致的数据,以决定要变更的部份,或者您可 以设计程式自动读取新的资料,并重复扣款业务流程,直到数据可以更新为止,这一切可以在后台执行,而不用让您的客户知道。在其它架构中也可通过这种思路来 实现乐观锁,但版本控制和冲突的检测要在自己程序的程序中实现和维护。

  2.悲观锁(Pessimistic Locking)

  虽然乐观锁能够提高系统的性能,但它是对发生冲突的访问进行事后的补救,应用在用户输入数据量很少的场合比较适合,但如果在 企业ERP,用户与系统交互涉及大量数据在页面表单上录入,如果事后提交失败后才提示用户要重新录入是很不现实的,所以有必要进行事前控制,这就要采用悲观锁。

  在多个客户端可能读取同一笔数据或同时更新一笔数据的情况下,防止同一个数据被修改而造成混乱,最简单的手段就是在读取时对数据进行锁定,其它客户端不能对同一笔数据进行更  新的读取动作。

  悲观锁定(Pessimistic Locking)一如其名称所示,悲观的认定每次资料存取时,其它的客户端也会存取同一笔数据,因此对该笔数据进行事先锁定,直到自己操作完成后解除锁定。

  悲观锁定通常透过系统或数据库本身的功能来实现,依赖系统或数据库本身提供的锁定机制,Hibernate即是如此,我们可以利用Query或Criteria的setLockMode()方法来设定要锁定的表或列(row)及其锁定模式,锁定模式有以下的几个:     

  LockMode.WRITE:在insert或update时进行锁定,Hibernate会在save()方法时自动获得锁定。

  LockMode.UPGRADE:利用SELECT … FOR UPDATE进行锁定。

  LockMode.UPGRADE_NOWAIT:利用SELECT … FOR UPDATE NOWAIT进行锁定,在Oracle环境下使用。

  LockMode.READ:在读取记录时Hibernate会自动获得锁定。

  LockMode.NONE:没有锁定。

  也可以在使用Session的load()或是lock()时指定锁定模式以进行锁定。

  如果数据库不支持所指定的锁定模式,Hibernate会选择一个合适的锁定替换,而不是丢出一个例外。

  3.其它构架中悲观锁的实现

  Hibernate的悲观锁,也是基于数据库的锁机制实现。下面的代码实现了对“用户”查询记录的加锁:

String sqlStr = "from userInfo as user where user.userId=’admin’";
Query query = session.createQuery(sqlStr);

query.setLockMode("user",LockMode.UPGRADE); //加锁

List userList = query.list();//执行查询,获取数据

query.setLockMode对查询语句中,特定别名所对应的记录进行加锁(我们为userInfo类指定了一个别名“user”),这里也就是对返回的所有user记录进行加锁:

select tuser0_.id as id, tuser0_.userId as userId, tuser0_.sex as sex from t_user  tuser0_
where (tuser0_.userId =’admin’ ) for update

  通过上述转换后的sql语句可知,Hibernate的加锁其实是利用了数据库的for update语句,在读取阶段对某条记录的锁定,而在更新阶段提交,释放锁

  其实其它架构也可以采取该思路,不过,数据库的for update语句的锁定和释放一定要在数据的同一个连接中,如果读取阶段和更新阶段不是统一连接,即读取之后断开了与数据库的连接,则for update语句的锁定立即失效,为此,如果其它架构中要采取这种方式则要做相应的调整。

  首先,由于Web应用是无状态的,也就是说数据库的for update语句的锁定和释放不一定是数据的同一个连接,为此,采用痕迹跟踪法:

在读取数据时生成唯一的序列号(serialId),建立与数据连接的映射,并放置一个map数据结构中;
在更新时,通过该serialId在连接池中重新获取该连接,用该连接去更新数据。

  如果系统是采用dao读取数据,实体bean去更新数据,则只要在更新数据之前断开读取数据时的连接,则可以通过其它途径更新数据,如下代码所示:

public void update (AbstractEntityData data, String[] selTeamName ,String serialId) 
throws Exception {dao.closeConnect(serialId);bo.update(data);}

  其中,dao.closeConnect(serialId)是断开数据连接,bo.update(data)是通过EJB更新数据库

  4.序列号(serialId)的创建和维护

  由于不同用户可能同时建立连接或同一用户先后建立连接,故创建序列号可以在读取数据时通过sessionId和时间戳组合而成。而在操作的过程 中,为了保持序列号不会丢失和唯一性,它不能放在session或application中,而是放在页面的request对象里,通过它向其它页面传 递。

  5.关联表的锁定

  其实,Hibernate的悲观锁方式只能对单个表的记录进行锁定,但现实中,存在关联更新的情况,即在更新主表的时候有可能会更新到与之相关的子表,与此同时,其它用户也可能通过其它主表更新相应的子表同一条记录。

  有两种方式处理,一是在读取数据通过sql语句关联子表相应记录,因为for update对所有关联表中符合条件的记录都会加锁;二是为子表找一个入口表,在更新子表的同时,必须更新子表的入口表。

  6.例外操作的处理

  采用这种方式,有一些例外情况必须小心处理,一是页面的关闭,如果调用相应的方法,如onbeforeunload()等,释放对应的数据库连 接;二是用户非正常关机退出系统,必须有数据库周期清除无用的连接,如间隔二十分钟等,来释放读取时对数据的锁定,否则,该数据会长时间被锁定,直至应用服务器重启。

  结论

  软件系统的并发控制一般是通过加锁来实现,同样,Web应用也是采用乐观锁和悲观锁来实现,乐观锁是一种事后补救措施,是通过程序的逻辑控制版 本来实现的,而悲观锁是事前的一种预防措施,它利用数据库的锁机制来实现,Hibernate对它做了一层封装,使应用更加方便,为了让其它架构都能适 用,本文还原了Hibernate的实现原理,提出一般的实现思路和注意实现。

 

Python 并发控制代码示例:

def concurrency_control(steam_uid):
    """
    并发控制
    :param steam_uid:
    :return:
    """
    try:
        u_lock = GameLock()
        u_lock.userid= userid
        u_lock.save()
    except BaseException as e:
        # client.captureException()
        return False
    now = datetime.now()
    today_str = now.strftime('%Y-%m-%d')
    GameLock.objects.filter(userid=userid,created__lt=today_str).delete()
    return True

 

© 著作权归作者所有

猿神出窍
粉丝 17
博文 186
码字总数 69230
作品 0
沙坪坝
项目经理
私信 提问
加载中

评论(1)

MockMan
MockMan
👍
6、Java并发性和多线程-并发性与并行性

以下内容转自http://tutorials.jenkov.com/java-concurrency/concurrency-vs-parallelism.html(使用谷歌翻译): 术语并发和并行性通常用于多线程程序。但是,并发和并行性究竟是什么意思呢...

easonjim
2017/06/16
0
0
LNMP的并发考虑与资源分配

在招聘中常问的一个问题 PHPer当被问到你的程序性能如何?程序的并发可以达到多少?程序的瓶颈在哪儿?为了满足业务需求应该购买多少台服务器?负载均衡中php应用服务器需要多少台? 可能这些...

tomener
2016/03/21
17
0
并发一:JAVA并发模型

一、并发 并发程序是指在运行中有两个及以上的任务同时在处理,与之相关的概念并行,是指在运行中有两个及以上的任务同时执行,差别是在于处理和执行。在单核CUP中两个及以上任务的处理方式是...

wangjie2016
2017/05/18
0
0
iOS 面试全方位剖析 -- 多线程篇

同步串行 先看一个头条的面试真题,下面这段代码有什么问题? 这是一个同步串行的问题,这段代码会造成程序死锁,下面分析一下为什么会造成程序死锁 上图中,首先向主队列中提交了一个 viewDidL...

PetitBread
2018/06/04
0
0
Java 10大优点—Part4—Java内存模型

在忙着参加在爱沙尼亚进行的 TEDx talk 演讲活动以及在比利时举办的一届非常忙碌的Devoxx 会议的间隙,我将继续推进 Java’s Rocking 的系列博文。 对还没有接触过这个系列博文的读者,不妨先...

foxlee
2013/12/09
339
1

没有更多内容

加载失败,请刷新页面

加载更多

Guava RateLimiter + AOP注解实现单机限流、统计QPS

1、基于springboot项目pom.xml添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency><d......

铁骨铮铮
20分钟前
3
0
龙芯版办公软件下载

金山wps office   rpm包:http://ftp.loongnix.org/os/loongnix/1.0/os/Packages/w/wps-office-10.8.0.6472-1.a20p1.mips64el.rpm   deb包:http://packages.deepin.com/loongson/pool/......

gugudu
26分钟前
2
0
BI报表分析和数据可视化,推荐这三个开源工具!

开源篇 一、Superset 1、技术架构:Python + Flask + React + Redux + SQLAlchemy 2、使用人群: (1)开发/分析人员做好看板,业务人员浏览看板数据 (2)业务人员可自行编辑图表,查看满足...

飓风2000
32分钟前
1
0
CountDownLatch

CountDownLatch的概念 CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。 CountDownLatch能够使一个线程在等待另外一些线程...

少年已不再年少
41分钟前
1
0
centos7 新手阿里云服务器安装mongodb

简介 MongoDB 是一个基于分布式 文件存储的NoSQL数据库 由C++语言编写,运行稳定,性能高 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案 MongoDB特点 模式自由 :可以把不同结构的文档存...

醉雨
52分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部