oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件
oracle中悲觀鎖定
回顧
在我以前的文章中 ,我解釋了使用顯式樂(lè)觀鎖定的好處。 然后我們發(fā)現(xiàn),在很短的時(shí)間范圍內(nèi),并發(fā)交易仍可以在我們當(dāng)前交易被提交之前立即提交產(chǎn)品價(jià)格更改。
此問(wèn)題可以描述如下:
- 愛(ài)麗絲拿產(chǎn)品
- 然后,她決定訂購(gòu)
- 獲得產(chǎn)品樂(lè)觀鎖
- 訂單已插入當(dāng)前交易數(shù)據(jù)庫(kù)會(huì)話(huà)中
- 產(chǎn)品版本由Hibernate顯式樂(lè)觀鎖定例程檢查
- 價(jià)格引擎設(shè)法提交產(chǎn)品價(jià)格更改
- 承諾進(jìn)行Alice交易而未意識(shí)到產(chǎn)品價(jià)格剛剛改變
復(fù)制問(wèn)題
因此,我們需要一種在樂(lè)觀鎖支票和訂單交易提交之間注入產(chǎn)品價(jià)格變化的方法。
在分析了Hibernate源代碼之后,我們發(fā)現(xiàn)SessionImpl.beforeTransactionCompletion()方法正在內(nèi)部actionQueue階段處理程序(檢查顯式樂(lè)觀鎖定實(shí)體版本)之后緊接著調(diào)用當(dāng)前配置的Interceptor.beforeTransactionCompletion()回調(diào):
public void beforeTransactionCompletion(TransactionImplementor hibernateTransaction) {LOG.trace( "before transaction completion" );actionQueue.beforeTransactionCompletion();try {interceptor.beforeTransactionCompletion( hibernateTransaction );}catch (Throwable t) {LOG.exceptionInBeforeTransactionCompletionInterceptor( t );} }有了這些信息,我們可以設(shè)置一個(gè)測(cè)試來(lái)復(fù)制我們的比賽條件:
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) {}運(yùn)行它時(shí),測(cè)試將生成以下輸出:
#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因此,比賽條件是真實(shí)的。 由您決定當(dāng)前的應(yīng)用程序是否需要更強(qiáng)的數(shù)據(jù)完整性要求,但是根據(jù)經(jīng)驗(yàn),安全性要好于遺憾。
解決問(wèn)題
要解決此問(wèn)題,我們只需要在結(jié)束事務(wù)處理方法之前添加一個(gè)悲觀的鎖定請(qǐng)求即可。
@Override protected void lockUpgrade(Session session, Product product) {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product); }顯式共享鎖將防止對(duì)我們之前樂(lè)觀地鎖定的實(shí)體進(jìn)行并發(fā)寫(xiě)入。 使用此方法,在釋放此鎖之前(在提交或回滾當(dāng)前事務(wù)之后),沒(méi)有其他并發(fā)事務(wù)可以更改產(chǎn)品。
有了新的悲觀鎖定請(qǐng)求,先前的測(cè)試將產(chǎn)生以下輸出:
#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也只能執(zhí)行FOR UPDATE排他鎖,這等效于顯式的PESSIMISTIC_WRITE鎖模式。
結(jié)論
如果您想知道為什么我們?cè)诋?dāng)前事務(wù)中同時(shí)使用樂(lè)觀鎖定和悲觀鎖定,則必須記住, 樂(lè)觀鎖定是多請(qǐng)求對(duì)話(huà)唯一可行的并發(fā)控制機(jī)制。
在我們的示例中,第一個(gè)請(qǐng)求使用只讀事務(wù)加載了Product實(shí)體。 產(chǎn)品實(shí)體具有關(guān)聯(lián)的版本,并且在寫(xiě)時(shí)事務(wù)期間將樂(lè)觀地鎖定此讀時(shí)實(shí)體快照。
悲觀鎖僅在寫(xiě)時(shí)事務(wù)期間有用,以防止在檢查產(chǎn)品實(shí)體版本后發(fā)生任何并發(fā)更新。 因此,邏輯鎖和物理鎖都可以協(xié)同工作以確保訂單價(jià)格數(shù)據(jù)的完整性。
在我撰寫(xiě)此博客文章時(shí), Java冠軍 Markus Eisele 接受了有關(guān)Hibernate Master Class計(jì)劃的采訪(fǎng) 。 在采訪(fǎng)中,我試圖解釋當(dāng)前的帖子示例,同時(shí)強(qiáng)調(diào)了解參考文檔之外的工具的真正重要性。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/02/fix-optimistic-locking-race-conditions-pessimistic-locking.html
oracle中悲觀鎖定
總結(jié)
以上是生活随笔為你收集整理的oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java多线程 异常处理_Java8多线
- 下一篇: 为什么非阻塞io性能更好_提高性能:流的