介绍

在我以前的文章中 ,我介绍了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锁定模式如何工作相关推荐

  1. Hibernate锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作

    介绍 在上 一篇 文章中 ,我介绍了OPTIMISTIC_FORCE_INCREMENT锁定模式,并将其应用于将子实体版本更改传播到锁定的父实体. 在本文中,我将介绍PESSIMISTIC_FORCE ...

  2. 休眠锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

    介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改. 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的 ...

  3. 休眠锁定模式–乐观锁定模式如何工作

    显式乐观锁定 在上一篇文章中 ,我介绍了Java持久性锁定的基本概念. 隐式锁定机制可防止丢失更新 ,它适用于我们可以主动修改的实体. 虽然隐式乐观锁定是一种广泛使用的技术,但是很少有人了解显式乐观锁 ...

  4. Hibernate锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

    介绍 在我以前的文章中 ,我解释了OPTIMISTIC锁定模式是如何工作的,以及它如何帮助我们同步外部实体状态更改. 在本文中,我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的 ...

  5. mysql事务模式怎么查_Mysql InnoDB中的查询事务模式与锁定select ..for update

    在 InnoDB 的行锁中使用所谓的 next-key locking.这就意味着,除了索引记录外,InnoDB 还可以锁定该索引记录前部"间隙" ('gap') 以阻塞其它用户在 ...

  6. win7系统没有计算机睡眠状态,win7旗舰版系统休眠模式和睡眠模式的区别

    大家都知道win7旗舰版系统有休眠模式和睡眠模式,但大多人不知道具体是什么意思,更不了解电脑休眠模式和睡眠模式有什么不同,误以为作用是相同的,两者的功能以及如何唤醒电脑的方式是完全不同的.通过开始菜单 ...

  7. win10 休眠设置无效_睡眠模式在Windows 10系统上不起作用?

    睡眠模式在Windows 10上不起作用?据报道,Windows 10存在许多关于睡眠模式的问题,例如睡眠模式丢失或灰显或睡眠模式失效而没有确切的原因. 由于睡眠模式在Windows 10中非常重要, ...

  8. STC12系列单片机的空闲模式、休眠模式(又叫掉电模式、停机模式)

    1. 空闲模式 空闲模式是指只有单片机不工作的状态.此时CPU无时钟停止工作,但是外部中断.外部低压检测电路.定时器.A/D转换.串行口等仍正常运行. 在空闲模式下,RAM.堆栈指针(SP).程序计数 ...

  9. 对称加密、工作模式和填充模式

    对称加密密钥长度分析 DES秘钥长度:8个字符 AES秘钥长度:16个字符 DES加密后密文长度是8的整数倍 AES加密后密文长度是16的整数倍 工作模式和填充模式 IOS加密,android没有解密 ...

最新文章

  1. 计算机图形学曲线生成原理,计算机图形学_曲线及生成.ppt
  2. LUOGU P2764 最小路径覆盖问题 (最小路径点覆盖)
  3. CVPR19 基于图卷积网络的多标签图像识别模型 论文笔记
  4. LeetCode 95. 不同的二叉搜索树 II(递归)
  5. 面试官:你说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景?
  6. TF-卷积函数 tf.nn.conv2d 介绍
  7. 2017 开源软件排行_2017年最佳开源教程
  8. android开发框架_2019 年五大跨平台移动应用开发工具
  9. 武昌工学院计算机专业学费,2016年武昌工学院学费专业收费情况及综合排名
  10. LoadRunner 录制常见错误解决方法
  11. VASP服务器第一次安装各种软件(上)
  12. 使用matlab解决收益和风险问题 数学建模算法与应用
  13. thinkphp 下载txt文档
  14. 西安中学2021文科高考成绩查询,2021年西安重点高中名单及排名,西安高中高考成绩排名榜...
  15. 偏微分方程数值求解 -- ING
  16. 【微信聊天机器人】基于python实现的PC端个人微信聊天机器人
  17. 龙珠直播php,斗鱼、全民TV、龙珠等直播平台排行榜 看视频直播发展趋势
  18. UE4编辑器修改界面显示语言
  19. php 504网关,php出现504错误的原因是什么
  20. HTML钢琴效果,JS实现钢琴效果

热门文章

  1. Photoshop图像修饰工具
  2. struts基本概念(1)
  3. java实现打印菱形
  4. MySQL中有外键时数据表的删除方法
  5. Mybatis insert操作细节【ID】
  6. git 拉取gitlab代码
  7. springboot2.5.5配置druid数据源1.2.8与jdbc
  8. 微信JS-SDK实现分享功能
  9. quarkus_使用Quarkus调试容器中的系统测试(视频)
  10. java xmpp_Java XMPP负载测试工具