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中悲觀鎖定
總結
以上是生活随笔為你收集整理的oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java多线程 异常处理_Java8多线
- 下一篇: 安卓免流量软件(安卓免流量)