軟件事務內存導論(八)提交和回滾事件
提交和回滾事件
Java的try-catch-finally語法結構不但使我們可以安全地處理異常,還能夠在程序拋出異常時選擇性地執行一些代碼。同樣地,我們 也可以控製程序在事務成功提交之後去執行某段代碼,而當事務回滾時則去執行另一段代碼。StmUtils中的deferred()和 compensatiing()這兩個函數分別提供了上述功能。特別地,在實現事務的過程中,為保證事務能順利完成,我們通常會加入一些帶副作用的邏輯, 而deferred()函數則是一個執行所有這部分邏輯的絕佳地點。
Java中的提交和回滾事件
我們可以把想要在事務成功完成之後執行的代碼放在Runnable接口實現部分的代碼塊中,並將其作為參數傳給StmUtils的 deferred()函數。同樣地,我們也可以把想要在事務失敗之後執行的代碼封裝在Runnable接口中傳給compensating()函數。由於 這兩個函數必須在事務的環境下運行,所以我們隻有在automically()函數的函數體中才能調用他們。
public class Counter { private final Ref<Integer> value = new Ref<Integer>(1); public void decrement() { new Atomic<Integer>() { public Integer atomically() { deferred(new Runnable() { public void run() { System.out.println( "Transaction completed...send email, log, etc."); } }); compensating(new Runnable() { public void run() { System.out.println("Transaction aborted...hold the phone"); } }); if(value.get() <= 0) throw new RuntimeException("Operation not allowed"); value.swap(value.get() - 1); return value.get(); } }.execute(); } }
在Counter類的定義代碼中我們看到,Counter類僅含有一個名為decrement()的實例方法。在這個方法中,我們繼承了 Atomic類並實現了atomically()函數。在前麵的例子中,我們都僅僅是簡單地把事務的邏輯代碼放在這個位置。而現在,除了原有的邏輯代碼之 外,我們把事務成功和事務回滾之後要執行的代碼也放到了atomically()裏麵。下麵讓我們構建一個簡單的測試用例來驗證一下Counter的功 能:
package com.agiledeveloper.pcj; public class UseCounter { public static void main(final String[] args) { Counter counter = new Counter(); counter.decrement(); System.out.println("Let's try again..."); try { counter.decrement(); } catch(Exception ex) { System.out.println(ex.getMessage()); } } }
通過運行UseCounter,我們可以清楚地觀察到事務成功完成和失敗時程序的執行邏輯:
Transaction aborted...hold the phone Transaction completed...send email, log, etc. Let's try again... Transaction aborted...hold the phone Operation not allowed
當第一次調用decrement()函數並成功完成事務之後,封裝在deferred()函數內的代碼邏輯將被執行。而當我們第二次調用 decrement()時,由於事務執行過程中拋出了異常,所以事務將被回滾,而封裝在compensating()函數內的代碼也將被執行。最後我們需 要注意的是,輸出結果中最頂部的那個非預期的重試是由我們之前在6.9節中曾討論過的默認優化設置所導致的。
deferred()函數是一個執行事務收尾工作以便使其效果固化的絕佳地點,所以我們可以在裏麵隨便進行打印、顯示消息、發布通知以及提交數據庫 事務等操作。如果我們在事務之外有什麼遺留的工作待完成,那麼這個函數無疑是最好的完成地點。與deferred()類似的 是,compensating()函數是記錄事務失敗信息的好地方。此外,如果我們之前已經將非托管對象(即那些沒有使用Akka Ref進行管理的對象)與托管對象混雜在一起的話,那麼這裏也是糾正這一錯誤的合適地點——但是由於這種做法太容易出錯,所以請你最好避免采用這樣的設計 思路。
Scala中的提交和回滾事件
在Scala中,我們處理提交和回滾事件的方式與Java基本相同,唯一區別就是在Scala中我們可以將閉包/函數值直接傳遞給deferred()和compensating()。下麵讓我們將Counter類由Java轉譯成Scala。
class Counter { private val value = Ref(1) def decrement() = { atomic { deferred { println("Transaction completed...send email, log, etc.") } compensating { println("Transaction aborted...hold the phone") } if(value.get() <= 0) throw new RuntimeException("Operation not allowed") value.swap(value.get() - 1) value.get() } } }
在上麵的代碼中,我們將事務運行成功時所要執行的那部分代碼封裝在一個閉包中,然後將其作為參數傳遞給deferred()函數。類似地,事務回滾 時所要執行的代碼也被作為一個閉包賦給了compensating()函數。與此同時,這兩個函數又與事務邏輯代碼一起被置於表示atomic()函數的 閉包當中。這段代碼再次彰顯了Scala在語法上簡潔明了的特征。下麵讓我們將UseConuter類也從Java轉譯成Scala:
package com.agiledeveloper.pcj object UseCounter { def main(args : Array[String]) : Unit = { val counter = new Counter() counter.decrement() println("Let's try again...") try { counter.decrement() } catch { case ex => println(ex.getMessage()) } } }
如下所示,Scala版代碼的執行結果與Java版的結果是完全相同的:
Transaction aborted...hold the phone Transaction completed...send email, log, etc. Let's try again... Transaction aborted...hold the phone Operation not allowed
文章轉自 並發編程網-ifeve.com
最後更新:2017-05-22 16:02:23
上一篇:
Java 7 Concurrency Cookbook – Javier Fernández González -前言
下一篇:
Java PermGen 去哪裏了?
PostGIS空間數據庫SRID背景知識 - 地理坐標係(球麵坐標係)和投影坐標係(平麵坐標係)
Centos6.6下升級Python
“百煉成鋼”的科技內蘊
Google發布新的開源編程語言GO
采用一個自創的"驗證框架"實現對數據實體的驗證[設計篇]
阿裏雲總裁胡曉明40個精彩回答 涉及CDN、價格戰、生態競爭等
理解TCP/IP,SOCKET,HTTP,FTP,RMI,RPC,webservice等的含義和關係
甘肅銅佛像 甘肅銅佛像廠家 甘肅東方三聖佛像
Project has no default.properties file! Edit the project properties to set one.錯誤解決方法
Java中的final