如何使用悲观锁定修复乐观锁定竞争条件

概括

在我以前的文章中 ,我解释了使用显式乐观锁定的好处。 然后我们发现,在很短的时间范围内,并发交易仍可以在我们当前交易被提交之前立即提交产品价格更改。

此问题可以描述如下:

显式锁定锁模式乐观竞争条件

  • 爱丽丝拿产品
  • 然后,她决定订购
  • 获得产品乐观锁
  • 订单已插入当前交易数据库会话中
  • 产品版本由Hibernate显式乐观锁定例程检查
  • 价格引擎设法提交产品价格更改
  • 承诺进行Alice交易而未意识到产品价格刚刚改变

复制问题

因此,我们需要一种在乐观锁支票和订单交易提交之间注入产品价格变化的方法。

在分析了Hibernate源代码之后,我们发现SessionImpl.beforeTransactionCompletion()方法正在内部actionQueue阶段处理程序(检查显式乐观锁定实体版本)之后紧接着调用当前配置的Interceptor.beforeTransactionCompletion()回调:

public void beforeTransactionCompletion(TransactionImplementor hibernateTransaction) {LOG.trace( "before transaction completion" );actionQueue.beforeTransactionCompletion();try {interceptor.beforeTransactionCompletion( hibernateTransaction );}catch (Throwable t) {LOG.exceptionInBeforeTransactionCompletionInterceptor( t );}
}

有了这些信息,我们可以设置一个测试来复制我们的比赛条件:

private AtomicBoolean ready = new AtomicBoolean();
private final CountDownLatch endLatch = new CountDownLatch(1);@Override
protected Interceptor interceptor() {return new EmptyInterceptor() {@Overridepublic void beforeTransactionCompletion(Transaction tx) {if(ready.get()) {LOGGER.info("Overwrite product price asynchronously");executeNoWait(new Callable<Void>() {@Overridepublic Void call() throws Exception {Session _session = getSessionFactory().openSession();_session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {try(PreparedStatement ps = connection.prepareStatement("UPDATE product set price = 14.49 WHERE id = 1")) {ps.executeUpdate();}}});_session.close();endLatch.countDown();return null;}});try {LOGGER.info("Wait 500 ms for lock to be acquired!");Thread.sleep(500);} catch (InterruptedException e) {throw new IllegalStateException(e);}}}};
}@Test
public void testExplicitOptimisticLocking() throws InterruptedException {try {doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {final Product product = (Product) session.get(Product.class, 1L, new LockOptions(LockMode.OPTIMISTIC));OrderLine orderLine = new OrderLine(product);session.persist(orderLine);lockUpgrade(session, product);ready.set(true);} catch (Exception e) {throw new IllegalStateException(e);}return null;}});} catch (OptimisticEntityLockException expected) {LOGGER.info("Failure: ", expected);}endLatch.await();
}protected void lockUpgrade(Session session, Product product) {}

运行它时,测试将生成以下输出:

#Alice selects a Product
DEBUG [main]: Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} #Alice inserts an OrderLine
DEBUG [main]: Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} 
#Alice transaction verifies the Product version
DEBUG [main]: Query:{[select version from product where id =?][1]} #The price engine thread is started
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Overwrite product price asynchronously
#Alice thread sleeps for 500ms to give the price engine a chance to execute its transaction
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Wait 500 ms for lock to be acquired!#The price engine changes the Product price
DEBUG [pool-1-thread-1]: Query:{[UPDATE product set price = 14.49 WHERE id = 1][]} #Alice transaction is committed without realizing the Product price change
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

因此,比赛条件是真实的。 由您决定当前的应用程序是否需要更强的数据完整性要求,但是根据经验,安全性胜过遗憾。

解决问题

要解决此问题,我们只需要在结束事务处理方法之前添加一个悲观的锁定请求即可。

@Override
protected void lockUpgrade(Session session, Product product) {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
}

显式共享锁将防止对我们之前乐观地锁定的实体进行并发写入。 使用此方法,在释放此锁之前(在提交或回滚当前事务之后),没有其他并发事务可以更改产品。

显式锁定锁模式乐观竞争条件修复

有了新的悲观锁定请求,先前的测试将产生以下输出:

#Alice selects a Product
DEBUG [main]: Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} #Alice inserts an OrderLine
DEBUG [main]: Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} #Alice applies an explicit physical lock on the Product entity
DEBUG [main]: Query:{[select id from product where id =? and version =? for update][1,0]} #Alice transaction verifies the Product version
DEBUG [main]: Query:{[select version from product where id =?][1]} #The price engine thread is started
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Overwrite product price asynchronously
#Alice thread sleeps for 500ms to give the price engine a chance to execute its transaction
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Wait 500 ms for lock to be acquired!#The price engine cannot proceed because of the Product entity was locked exclusively, so Alice transaction is committed against the ordered Product price
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#The physical lock is released and the price engine can change the Product price
DEBUG [pool-1-thread-1]: Query:{[UPDATE product set price = 14.49 WHERE id = 1][]}

即使我们要求使用PESSIMISTIC_READ锁,HSQLDB也只能执行FOR UPDATE排他锁,等效于显式的PESSIMISTIC_WRITE锁模式。

结论

如果您想知道为什么我们在当前事务中同时使用乐观锁定和悲观锁定,则必须记住, 乐观锁定是用于多请求对话的唯一可行的并发控制机制。

在我们的示例中,第一个请求使用只读事务加载了Product实体。 产品实体具有关联的版本,并且在写时事务期间将乐观地锁定此读时实体快照。

悲观锁仅在写时事务期间有用,以防止在检查产品实体版本后发生任何并发更新。 因此,逻辑锁和物理锁都可以协同工作以确保订单价格数据的完整性。

当我撰写此博客文章时, Java冠军 Markus Eisele 接受了有关Hibernate Master Class计划的采访 。 在采访中,我试图解释当前的帖子示例,同时强调了解参考文档之外的工具的真正重要性。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2015/02/fix-optimistic-locking-race-conditions-pessimistic-locking.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360598.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SWT外观:自定义FlatScrollBar颜色等

最近&#xff0c;我引入了一个自定义滑块控件 &#xff0c;该控件可用于改善SWT外观和更细微的视图布局的感觉。 令人高兴的是&#xff0c;该小部件似乎已经在Code Affine世界之外找到了较早的采用者 。 这导致了一些增强 &#xff0c;这些增强将在以下各节中介绍。 SWT滚动条…

java虚拟机编译_[四] java虚拟机JVM编译器编译代码简介 字节码指令实例 代码到底编译成了什么形式...

前言简介前文已经对虚拟机进行过了简单的介绍,并且也对class文件结构,以及字节码指令进行了详尽的说明想要了解JVM的运行机制,以及如何优化你的代码,你还需要了解一下,java编译器到底是如何编译你的代码的本文不是从最底层的编译原理讲解本文是针对java代码,去查看归纳总结编译…

提高性能:流的非阻塞处理

1.简介 想象一下&#xff0c;我们有一个需要访问外部Web服务的应用程序&#xff0c;以便收集有关客户端的信息&#xff0c;然后对其进行处理。 更具体地说&#xff0c;我们无法在一次调用中获得所有这些信息。 如果我们要查找不同的客户端&#xff0c;则需要多次调用。 如下图…

CDI和EJB:在事务成功时发送异步邮件

再一次问好&#xff01; :) 这次&#xff0c;我选择了一项常见任务&#xff0c;我认为大多数情况下都以错误的方式完成&#xff1a;发送电子邮件。 并非所有人都不知道电子邮件API的工作方式&#xff0c;例如JavaMail或Apache的commons-email 。 我通常看到的一个问题是&#…

使用默认方法的界面演变–第一部分:方法

几周前&#xff0c;我们详细研究了默认方法 -Java 8中引入的一项功能&#xff0c;该功能允许为接口方法提供实现&#xff0c;即方法主体&#xff0c;从而定义接口中的行为。 引入此功能是为了实现接口演进 。 在JDK的上下文中&#xff0c;这意味着在不破坏所有代码的情况下向接…

java两个和三个_Java语言基础(day_03)

数据类型中补充的几个小问题1)在定义Long或者Float类型变量的时候&#xff0c;要加L或者f。整数默认是int类型&#xff0c;浮点数默认晨double。byte&#xff0c;short在定义的肘候&#xff0c;他们接收的某实是一个int类型的值。这个是自己做了一个数据检测的&#xff0c;如果…

在Websphere 8.0上安装Liferay 6.2 Enterprise Edition

为Liferay准备Websphere 当应用服务器二进制文件均已安装完毕&#xff0c;启动WebSphere应用服务器&#xff08;WAS&#xff09; 配置文件管理工具来创建一个配置文件适用于Liferay的和遵循的指示说明这里正式Liferay的文档。 这些说明用于在Websphere 8.5上安装Liferay 6.2&am…

java完数流程图_编程基本功训练:流程图画法及练习

对于“程序设计”的工作&#xff0c;许多初学者的理解就是“写代码”。同样&#xff0c;新手们苦恼的问题是&#xff0c;他们只会“写代码”。当接到一个新的任务&#xff0c;不少人总是在第一时间就爬到键盘上去敲代码。敲着敲着&#xff0c;就把自己绕糊涂了。头晕脑胀地坚持…

java开发环境搭建 pdf_01搭建java web开发环境.pdf

01搭建java web开发环境.pdf还剩19页未读&#xff0c;继续阅读下载文档到电脑&#xff0c;马上远离加班熬夜&#xff01;亲&#xff0c;很抱歉&#xff0c;此页已超出免费预览范围啦&#xff01;如果喜欢就下载吧&#xff0c;价低环保&#xff01;内容要点&#xff1a;( 7)在 M…

在AWS Elastic MapReduce上运行PageRank Hadoop作业

在上一篇文章中&#xff0c;我描述了执行PageRank计算的示例&#xff0c;该示例是使用Apache Hadoop进行Mining Massive Dataset课程的一部分。 在那篇文章中&#xff0c;我接受了Java中现有的Hadoop作业&#xff0c;并做了一些修改&#xff08;添加了单元测试&#xff0c;并通…

java sort 第二个参数_详解java Collections.sort的两种用法

Collections是一个工具类&#xff0c;sort是其中的静态方法&#xff0c;是用来对List类型进行排序的&#xff0c;它有两种参数形式&#xff1a;public static > void sort(List list) {list.sort(null);}public static void sort(List list, Comparator super T> c) {lis…

MVC3 中使用Unity实现依赖注入

前言&#xff1a;前段时间一直在研究依赖注入&#xff0c;不过不是在MVC框架中使用&#xff0c;今天突然想到在MVC中使用Unity实现依赖注入&#xff0c;一时慌了&#xff0c;不知道从何下手&#xff0c;接着就是网上不停的找资料&#xff0c;下面我把我找到的资料分享下&#x…

ms2005 SQL Server设置改为SQL Server身份验证

1.为 SQL Server 2005 Express Edition 或 SQL Server 2005 Developer Edition 启用远程连接 必须为要从远程计算机连接到的每个 SQL Server 2005 实例启用远程连接。为此&#xff0c;请按照下列步骤操作&#xff1a; 1.单击“开始”&#xff0c;依次指向“程序”、“Microsoft…

在Graphite中存储Hystrix的几个月历史指标

Hystrix的杀手级功能之一是低延迟&#xff0c;数据密集型且美观的仪表板 &#xff1a; 即使这只是Hystrix实际操作的副作用&#xff08;断路器&#xff0c;线程池&#xff0c;超时等&#xff09;&#xff0c;它也往往是最令人印象深刻的功能。 为了使其工作&#xff0c;您必须…

NYOJ-----最少乘法次数

最少乘法次数 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;3描述给你一个非零整数&#xff0c;让你求这个数的n次方&#xff0c;每次相乘的结果可以在后面使用&#xff0c;求至少需要多少次乘。如24&#xff1a;2*222&#xff08;第一次乘&a…

php投票系统中各个文件的作用说明,PHP开发简单投票系统之投票页面功能模块(二)...

当完成前面的投票后&#xff0c;可以选择点击查看结果查看每个项目的总票数和所有项目的投票百分比。点击“查看结果”后程序会自动计算每个项目的票数和所占百分比。使用了隐藏表单属性隐藏域在页面中对于用户是不可见的&#xff0c;在表单中插入隐藏域的目的在于收集或发送信…

ref 和out 关键字

ref 和out 关键字 通过对CLR的学习&#xff0c;我们可以知道&#xff0c;CLR默认所有方法参数都是传值的。对于引用类型的对象&#xff0c;传递的是对象的引用&#xff08;指向对象的指针&#xff09;&#xff0c;被调用者拥有该对象的引用的拷贝&#xff0c;能够修改对象&…

php-cli下载,php-cli-color

一个简单的 PHP 命令行 cli 输出彩色的类库安装composer require wujunze/php-cli-color ~1.0使用getColoredString("Testing Colors class, this is purple string on yellow background.", "purple", "yellow") . PHP_EOL;echo $colors->ge…

您会后悔对Lambdas应用重载!

编写好的API很难。 非常辛苦。 如果您想让用户喜欢您的API&#xff0c;则必须考虑很多事情。 您必须在以下两者之间找到适当的平衡&#xff1a; 有用性 易用性 向后兼容 前向兼容性 之前&#xff0c;在我们的文章&#xff1a; 如何设计良好的常规API中&#xff0c;我们已经…

如何:优化Hive ZooKeeper Lock Manager实施

Hive一直使用ZooKeeper作为分布式锁定管理器来支持HiveServer2中的并发。 基于ZooKeeper的锁管理器在小型环境中运行良好。 但是&#xff0c;随着越来越多的用户从HiveServer迁移到HiveServer2并开始创建大量并发会话&#xff0c;可能会出现问题。 主要问题是Hiveserver2和ZooK…