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

oracle中悲观锁定

回顾

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

此问题可以描述如下:

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

  • 爱丽丝拿产品
  • 然后,她决定订购
  • 获得产品乐观锁
  • 订单已插入当前交易数据库会话中
  • 产品版本由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

oracle中悲观锁定

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

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

相关文章

初学者宝典:C语言入门基础知识大全(中)

04常量在程序运行中&#xff0c;其值不能被改变的量称为常量。常量有5种类型&#xff1a;整型常量、实型常量、字符常量、字符串常量和符号常量。4.1 数值转换—数值的四种表现形式&#xff1a;①&#xff1a;二进制&#xff1a;所有数字由0,1构成&#xff0c;逢二进一&#xf…

linux命令创建硬链接,Linux终端命令接口(十一)硬链接与软连接

一、进入终端 输入 使用 ls -l 即可.示例:$ touch file1 # 创建新文件 file1$ touch file2 # 创建新文件 file2$ ln file1 file3 # 为 file1 创建硬链接 file3$ ls -ltotal 0-rw-r--r-- 2 root root 0 01-25 16:59 file1-rw-r--r-- 1 root root 0 01-25 17:00 f…

stateless_Spring Stateless State Security第3部分:JWT +社会认证

stateless我的Stateless Spring Security系列文章的第三部分也是最后一部分是关于将基于JWT令牌的身份验证与spring-social-security混合在一起的。 这篇文章直接建立在它的基础上&#xff0c;并且主要集中在已更改的部分上。 想法是使用基于OAuth 2的“使用Facebook登录”功能…

return在c语言中是什么意思

函数是C语言的基本构件&#xff0c;一个C程序可以由一个主函数和若干个子程序函数构成&#xff0c;由主函数调用其它子程序函数&#xff0c;其他子程序函数也可以互相调用。通常希望通过函数调用使主函数能得到一个确定的值&#xff0c;这就是函数的返回值。在C语言中通过函数实…

为什么非阻塞io性能更好_提高性能:流的非阻塞处理

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

linux history文件路径,Linux、Unix常用命令(文件和目录相关)

mkdir dirname 建立子目录. 注意:用户不能在一个不存在的目录中建立子目录。mkdir data 在当前目录下建立子目录 datamkdir /usr/data 在/usr/目录下建立子目录 data&#xff0c;此时/usr 目录必须已经存在。rmdirrmdir dirname 删除空目录&#xff0c;目录里面如有文件或目录则…

c语言的输入函数有哪些

c语言的输入函数有&#xff1a;1、scanf的返回值scanf()函数返回成功赋值的数据项数&#xff0c;读到文件末尾出错时则返回EOF。如&#xff1a;scanf("%d%d", &a, &b);如果a和b都被成功读入&#xff0c;那么scanf的返回值就是2如果只有a被成功读入&#xff0…

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

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

linux中多进程调试,linux下用gdb调试多进程

今天来学习一下linux下gdb如何调试多进程&#xff0c;在学习之前我我们能先看一张表&#xff1a;这张表是gdb调试的命令表&#xff0c;这对那些对gdb不熟的同学来说是非常有必要的。一、多进程调试的命令1、set follow-fork-mode parent|child因为gdb在一般情况下&#xff0c;只…

初学者宝典:C语言入门基础知识大全(下)

06类型的自动转换和强制转换当同一表达式中各数据的类型不同时&#xff0c;编译程序会自动把它们转变成同一类型后再进行计算。转换优先级为&#xff1a;char < int < float < double 即左边级别“低“的类型向右边转换。具体地说&#xff0c;若在表达式中优先级最高的…

linux接口 头文件,第一种:1、添加关键头文件:#include linux/of_gpio.h#include linux/gpio.h...

第一种&#xff1a;1、添加关键头文件&#xff1a;#include #include #include #include #include #include 2、在已经存在驱动文件中搜索"DEVICE_ATTR"关键字&#xff0c;如果存在&#xff0c;直接参考已经存在的方法添加一个即可&#xff0c;如下&#xff1a;unsig…

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

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

C语言中scanf函数的3种常见问题与应对技巧

在写代码时难免对一些知识点不熟悉&#xff0c;导致犯错&#xff0c;今天分享几点小知识给大家。空白符问题#includeint main(void){int a;printf("input the data ");scanf("%d ",&a); //这里多了一个回车符printf("%d",a);return 0;}结果…

jpa和hibernate_JPA和Hibernate级联类型的初学者指南

jpa和hibernate介绍 JPA将实体状态转换转换为数据库DML语句。 由于对实体图进行操作很常见&#xff0c;因此JPA允许我们将实体状态更改从父级传播到子级 。 通过CascadeType映射配置此行为。 JPA与Hibernate级联类型 Hibernate支持所有JPA级联类型和一些其他旧式级联样式。 下…

linux find 权限不够,超级用户find: `/home/pipi/.gvfs': 权限不够

用sudo su命令切换成的根用户&#xff0c;在找某文件的时候报错&#xff1a;rootubuntu:/home/pipi# find / -perm -2000/sbin/unix_chkpwdfind: /home/pipi/.gvfs: 权限不够就是普通用户pipi的主目录下的一个叫 .gvfs 的目录&#xff0c;dr-x------ 2 pipi pipi 0 …

aws上部署hadoop_在AWS Elastic MapReduce上运行PageRank Hadoop作业

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

linux编写一个简单的端口扫描程序,小弟我在linux下写了个简单的多线程端口扫描程序,运行时出现有关问题,请问一下(2)...

当前位置:我的异常网 Linux/Unix 小弟我在linux下写了个简单的多线程端口扫描程序&#xff0c;小弟我在linux下写了个简单的多线程端口扫描程序&#xff0c;运行时出现有关问题,请问一下(2)www.myexceptions.net 网友分享于&#xff1a;2013-02-26 浏览&#xff1a;23次usle…

在嵌套使用if语句时,C语言规定else总是什么?

C语言的语法规定&#xff1a;else子句总是与前面最近的不带else的if相结合&#xff0c;与书写格式无关。在C语言中&#xff0c;使用if和else关键字对条件进行判断。请先看下面的代码&#xff1a;#include int main(){ int age; printf("请输入你的年龄&#xff1a;&…

optional空值判断_Java 8 Optional不仅用于替换空值

optional空值判断总览 在Java 8中&#xff0c;您可以返回Optional而不是返回null。 就像您在Java 7中所做的那样。这可能会有所不同&#xff0c;这取决于您是否倾向于忘记检查null还是使用静态代码分析来检查nullalbe引用。 但是&#xff0c;还有一种更引人注目的情况是将Opti…

continue语句的作用是结束整个循环的执行吗?

continue 语句的作用是结束本次循环&#xff0c;跳过循环体中剩余的语句而强制进入下一次循环&#xff08;回到循环体的开头准备再次执行循环体&#xff09;。continue语句只用在 while、for 循环中&#xff0c;常与 if 条件语句一起使用&#xff0c;判断条件是否成立。使用方式…