休眠锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作
介绍
在我以前的文章中 ,我介绍了OPTIMISTIC_FORCE_INCREMENT锁定模式,并将其应用于将子实体版本更改传播到锁定的父实体。 在本文中,我将介绍PESSIMISTIC_FORCE_INCREMENT锁定模式,并将其与乐观的锁定模式进行比较。
相像多于不同
正如我们已经发现的那样,即使当前事务没有修改锁定的实体状态, OPTIMISTIC_FORCE_INCREMENT锁定模式也可以增加实体版本。 对于每种锁定模式,Hibernate定义一个关联的LockingStrategy ,并且OPTIMISTIC_FORCE_INCREMENT锁定模式事件由OptimisticForceIncrementLockingStrategy处理:
public class OptimisticForceIncrementLockingStrategy implements LockingStrategy {//code omitted for brevity@Overridepublic void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session) {if ( !lockable.isVersioned() ) {throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );}final EntityEntry entry = session.getPersistenceContext().getEntry( object );// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.( (EventSource) session ).getActionQueue().registerProcess( new EntityIncrementVersionProcess( object, entry ) );}
}
此策略在当前持久性上下文操作队列中注册EntityIncrementVersionProcess 。 在完成当前正在运行的事务之前,锁定的实体版本会增加。
public class EntityIncrementVersionProcess implements BeforeTransactionCompletionProcess {//code omitted for brevity@Overridepublic void doBeforeTransactionCompletion(SessionImplementor session) {final EntityPersister persister = entry.getPersister();final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );entry.forceLocked( object, nextVersion );}
}
与OPTIMISTIC_FORCE_INCREMENT相似 , PESSIMISTIC_FORCE_INCREMENT锁定模式由PessimisticForceIncrementLockingStrategy处理:
public class PessimisticForceIncrementLockingStrategy implements LockingStrategy {//code omitted for brevity@Overridepublic void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session) {if ( !lockable.isVersioned() ) {throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );}final EntityEntry entry = session.getPersistenceContext().getEntry( object );final EntityPersister persister = entry.getPersister();final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );entry.forceLocked( object, nextVersion );}
}
锁定的实体立即增加,因此这两种锁定模式执行相同的逻辑,但时间不同。 PESSIMISTIC_FORCE_INCREMENT的命名可能使您想到您正在使用悲观的锁定策略,而实际上,此锁定模式只是一个乐观的锁定变体。
悲观锁需要显式物理锁(共享或独占),而乐观锁则依赖于当前事务隔离级别的隐式锁。
存储库用例
我将重用之前的练习,然后切换到使用PESSIMISTIC_FORCE_INCREMENT锁定模式。 回顾一下,我们的域模型包含:
- 一个存储库实体,其版本随每次新的提交而增加
- 一个Commit实体,封装了一个原子存储库状态转换
- 一个CommitChange组件,封装了一个存储库资源更改
防止并行修改
爱丽丝和鲍勃同时访问我们的系统。 从数据库中获取存储库实体后,它总是被锁定:
private final CountDownLatch startLatch = new CountDownLatch(1);
private final CountDownLatch endLatch = new CountDownLatch(1);@Test
public void testConcurrentPessimisticForceIncrementLockingWithLockWaiting() throws InterruptedException {LOGGER.info("Test Concurrent PESSIMISTIC_FORCE_INCREMENT Lock Mode With Lock Waiting");doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {Repository repository = (Repository) session.get(Repository.class, 1L);session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(repository);executeNoWait(new Callable<Void>() {@Overridepublic Void call() throws Exception {return doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session _session) {LOGGER.info("Try to get the Repository row");startLatch.countDown();Repository _repository = (Repository) _session.get(Repository.class, 1L);_session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(_repository);Commit _commit = new Commit(_repository);_commit.getChanges().add(new Change("index.html", "0a1,2..."));_session.persist(_commit);_session.flush();endLatch.countDown();return null;}});}});startLatch.await();LOGGER.info("Sleep for 500ms to delay the other transaction PESSIMISTIC_FORCE_INCREMENT Lock Mode acquisition");Thread.sleep(500);Commit commit = new Commit(repository);commit.getChanges().add(new Change("README.txt", "0a1,5..."));commit.getChanges().add(new Change("web.xml", "17c17..."));session.persist(commit);return null;} catch (InterruptedException e) {fail("Unexpected failure");}return null;}});endLatch.await();
}
该测试用例生成以下输出:
#Alice selects the Repository
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} #Alice locks the Repository using a PESSIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[update repository set version=? where id=? and version=?][1,1,0]} #Bob tries to get the Repository but the SELECT is blocked by Alice lock
INFO [pool-1-thread-1]: c.v.h.m.l.c.LockModePessimisticForceIncrementTest - Try to get the Repository row#Alice sleeps for 500ms to prove that Bob is waiting for her to release the acquired lock
Sleep for 500ms to delay the other transaction PESSIMISTIC_FORCE_INCREMENT Lock Mode acquisition#Alice makes two changes and inserts a new Commit<a href="https://vladmihalcea.files.wordpress.com/2015/02/explicitlockingpessimisticforceincrementfailfast.png"><img src="https://vladmihalcea.files.wordpress.com/2015/02/explicitlockingpessimisticforceincrementfailfast.png?w=585" alt="ExplicitLockingPessimisticForceIncrementFailFast" width="585" height="224" class="alignnone size-large wp-image-3955" /></a>
Query:{[insert into commit (id, repository_id) values (default, ?)][1]}
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,5...,README.txt]#The Repository version is bumped up to version 1 and a conflict is raised
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,17c17...,web.xml]} Query:{[update repository set version=? where id=? and version=?][1,1,0]}#Alice commits the transaction, therefore releasing all locks
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Bob Repository SELECT can proceed
Query:{[select lockmodepe0_.id as id1_2_0_, lockmodepe0_.name as name2_2_0_, lockmodepe0_.version as version3_2_0_ from repository lockmodepe0_ where lockmodepe0_.id=?][1]} #Bob can insert his changes
Query:{[update repository set version=? where id=? and version=?][2,1,1]}
Query:{[insert into commit (id, repository_id) values (default, ?)][1]}
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][2,0a1,2...,index.html]}
下图可以很容易地看到这个锁定过程:
每当修改数据库行时, HSQLDB测试数据库“ 两阶段锁定”实现都会使用过程粒度表锁定。
这就是Bob不能获得Alice刚刚更新的Repository数据库行上的读取锁的原因。 其他数据库(例如Oracle,PostgreSQL)使用MVCC ,因此允许SELECT继续执行(使用当前的修改事务撤消日志来重新创建前一个行状态),同时阻止冲突的数据修改语句(例如,当其他并发事务已经停止时更新存储库行)尚未提交锁定的实体状态更改)。
快速失败
即时版本增加具有一些有趣的好处:
- 如果版本UPDATE成功(获取了排他级锁),则其他任何并发事务都无法修改锁定的数据库行。 这是将逻辑锁(版本递增)升级为物理锁(数据库互斥锁)的时刻。
- 如果版本UPDATE失败(因为其他一些并发事务已经提交了版本更改),那么我们当前正在运行的事务可以立即回滚(而不是在提交期间等待事务失败)
后一个用例可以如下所示:
对于这种情况,我们将使用以下测试用例:
@Test
public void testConcurrentPessimisticForceIncrementLockingFailFast() throws InterruptedException {LOGGER.info("Test Concurrent PESSIMISTIC_FORCE_INCREMENT Lock Mode fail fast");doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {Repository repository = (Repository) session.get(Repository.class, 1L);executeAndWait(new Callable<Void>() {@Overridepublic Void call() throws Exception {return doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session _session) {Repository _repository = (Repository) _session.get(Repository.class, 1L);_session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(_repository);Commit _commit = new Commit(_repository);_commit.getChanges().add(new Change("index.html", "0a1,2..."));_session.persist(_commit);_session.flush();return null;}});}});session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(repository);fail("Should have thrown StaleObjectStateException!");} catch (StaleObjectStateException expected) {LOGGER.info("Failure: ", expected);}return null;}});
}
生成以下输出:
#Alice selects the Repository
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} #Bob selects the Repository too
Query:{[select lockmodepe0_.id as id1_2_0_, lockmodepe0_.name as name2_2_0_, lockmodepe0_.version as version3_2_0_ from repository lockmodepe0_ where lockmodepe0_.id=?][1]} #Bob locks the Repository using a PESSIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[update repository set version=? where id=? and version=?][1,1,0]} #Bob makes a change and inserts a new Commit
Query:{[insert into commit (id, repository_id) values (default, ?)][1]}
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,2...,index.html]} #Bob commits the transaction
DEBUG [pool-3-thread-1]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Alice tries to lock the Repository
Query:{[update repository set version=? where id=? and version=?][1,1,0]} #Alice cannot lock the Repository, because the version has changed
INFO [main]: c.v.h.m.l.c.LockModePessimisticForceIncrementTest - Failure:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.LockModePessimisticForceIncrementTest$Repository#1]
结论
像OPTIMISTIC_FORCE_INCREMENT一样, PESSIMISTIC_FORCE_INCREMENT锁定模式对于将实体状态更改传播到父实体非常有用。
尽管锁定机制相似,但是PESSIMISTIC_FORCE_INCREMENT可以当场应用,从而允许当前运行的事务即时评估锁定结果。
- 代码可在GitHub上获得 。
翻译自: https://www.javacodegeeks.com/2015/02/hibernate-locking-patterns-pessimistic_force_increment-lock-mode-work.html
休眠锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作相关推荐
- Hibernate锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作
介绍 在上 一篇 文章中 ,我介绍了OPTIMISTIC_FORCE_INCREMENT锁定模式,并将其应用于将子实体版本更改传播到锁定的父实体. 在本文中,我将介绍PESSIMISTIC_FORCE ...
- 休眠锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作
介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改. 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的 ...
- 休眠锁定模式–乐观锁定模式如何工作
显式乐观锁定 在上一篇文章中 ,我介绍了Java持久性锁定的基本概念. 隐式锁定机制可防止丢失更新 ,它适用于我们可以主动修改的实体. 虽然隐式乐观锁定是一种广泛使用的技术,但是很少有人了解显式乐观锁 ...
- Hibernate锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作
介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改. 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的 ...
- mysql事务模式怎么查_Mysql InnoDB中的查询事务模式与锁定select ..for update
在 InnoDB 的行锁中使用所谓的 next-key locking.这就意味着,除了索引记录外,InnoDB 还可以锁定该索引记录前部"间隙" ('gap') 以阻塞其它用户在 ...
- win7系统没有计算机睡眠状态,win7旗舰版系统休眠模式和睡眠模式的区别
大家都知道win7旗舰版系统有休眠模式和睡眠模式,但大多人不知道具体是什么意思,更不了解电脑休眠模式和睡眠模式有什么不同,误以为作用是相同的,两者的功能以及如何唤醒电脑的方式是完全不同的.通过开始菜单 ...
- win10 休眠设置无效_睡眠模式在Windows 10系统上不起作用?
睡眠模式在Windows 10上不起作用?据报道,Windows 10存在许多关于睡眠模式的问题,例如睡眠模式丢失或灰显或睡眠模式失效而没有确切的原因. 由于睡眠模式在Windows 10中非常重要, ...
- STC12系列单片机的空闲模式、休眠模式(又叫掉电模式、停机模式)
1. 空闲模式 空闲模式是指只有单片机不工作的状态.此时CPU无时钟停止工作,但是外部中断.外部低压检测电路.定时器.A/D转换.串行口等仍正常运行. 在空闲模式下,RAM.堆栈指针(SP).程序计数 ...
- 对称加密、工作模式和填充模式
对称加密密钥长度分析 DES秘钥长度:8个字符 AES秘钥长度:16个字符 DES加密后密文长度是8的整数倍 AES加密后密文长度是16的整数倍 工作模式和填充模式 IOS加密,android没有解密 ...
最新文章
- 计算机图形学曲线生成原理,计算机图形学_曲线及生成.ppt
- LUOGU P2764 最小路径覆盖问题 (最小路径点覆盖)
- CVPR19 基于图卷积网络的多标签图像识别模型 论文笔记
- LeetCode 95. 不同的二叉搜索树 II(递归)
- 面试官:你说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景?
- TF-卷积函数 tf.nn.conv2d 介绍
- 2017 开源软件排行_2017年最佳开源教程
- android开发框架_2019 年五大跨平台移动应用开发工具
- 武昌工学院计算机专业学费,2016年武昌工学院学费专业收费情况及综合排名
- LoadRunner 录制常见错误解决方法
- VASP服务器第一次安装各种软件(上)
- 使用matlab解决收益和风险问题 数学建模算法与应用
- thinkphp 下载txt文档
- 西安中学2021文科高考成绩查询,2021年西安重点高中名单及排名,西安高中高考成绩排名榜...
- 偏微分方程数值求解 -- ING
- 【微信聊天机器人】基于python实现的PC端个人微信聊天机器人
- 龙珠直播php,斗鱼、全民TV、龙珠等直播平台排行榜 看视频直播发展趋势
- UE4编辑器修改界面显示语言
- php 504网关,php出现504错误的原因是什么
- HTML钢琴效果,JS实现钢琴效果