軟件事務內存導論(六)配置Akka事務
配置Akka事務
默認情況下,Akka為其相關的運行參數都設定了默認值,我們可以通過代碼或配置文件akka.conf來更改這些默認設置。如果想了解如何指定或修改該配置文件位置的詳細信息,請參閱Akka的文檔。
針對單個事務,我們可以利用TransactionFactory在程序代碼中更改其設置。下麵就讓我們用這種方式先後在Java和Scala中更改一些設置來為你展示如何實現設置的變更。
在Java中對事務進行配置
public class CoffeePot { private static final Ref<Integer> cups = new Ref<Integer>(24); public static int readWriteCups(final boolean write) { final TransactionFactory factory = new TransactionFactoryBuilder().setReadonly(true).build(); return new Atomic<Integer>(factory) { public Integer atomically() { if(write) cups.swap(20); return cups.get(); } }.execute(); }
為了能夠用編程的方式對事務進行配置,我們需要一個TransactionFactory實例對象,而 TransactionFactoryBuilder則為我們提供了很多方便的函數用於創建該Factory。在上例中,我們創建了一個 TranactionFactoryBuilder實例對象,並調用該對象的setReadonly()函數來為TransactionFactory添 加readonly選項。由於TransactionFactoryBuilder實現了Cascade[1]設 計模式,所以我們可以將更多用於改變事務屬性的函數串在一起掛在TransactionFactoryBuilder構造函數之後、build()函數之 前。隨後我們把factory的實例對象作為Atomic的一個構造函數參數傳給它,這樣就保證了該事務內的所有操作都不會變更任何托管引用。
通過上述設置我們已經將readWriteCups()變成了一個隻讀事務,接下來你肯定希望了解在一個隻讀事務中試圖改變引用的值將會產生什麼後 果。下麵我們會調用兩次readWriteCups(),第一次僅僅是讀取cups引用的內容,而第二次調用則會嚐試改變cups引用的值。
public static void main(final String[] args) { System.out.println("Read only"); readWriteCups(false); System.out.println("Attempt to write"); try { readWriteCups(true); } catch(Exception ex) { System.out.println("Failed " + ex); } } }
由於被設置成了隻讀,所以readWriteCups()事務不歡迎變更請求。於是當我們試圖更改cups引用的值時,係統拋出了org.multiverse.api.exceptions.ReadonlyException異常,並且整個事務也將回滾。
Read only Attempt to write Failed org.multiverse.api.exceptions.ReadonlyException: Can't open for write transactional object 'akka.stm.Ref@1272670619' because transaction 'DefaultTransaction' is readonly'
上述運行時異常是在調用引用的swap()的時候拋出來的。該函數的作用是當且僅當新值與當前值不同時,將其引用改為指向新值的地址;否則,該函數 將忽略變更請求。所以在本例中,如果我們在調用swap()時將參數20換成與當前cpus引用的值相等的24,則係統就不會拋出任何異常。
在Scala中對事物進行配置
在Scala中,我們可以使用atomic()函數代替Atomic類來創建事務,該函數在使用時需要一個TransactionFactory類 型的可選參數。同時,由於我們能夠在夥伴對象(companion object)上使用工廠方法,所以創建factory實例也比在Java中要簡單許多。
object CoffeePot { val cups = Ref(24) def readWriteCups(write : Boolean) = { val factory = TransactionFactory(readonly = true) atomic(factory) { if(write) cups.swap(20) cups.get() } } def main(args : Array[String]) : Unit = { println("Read only") readWriteCups(false) println("Attempt to write") try { readWriteCups(true) } catch { case ex => println("Failed " + ex) } } }
除了在代碼方麵保持了Scala和Akka特有的簡潔優雅之外,上述代碼與Java版本就沒有什麼其他不同之處了,所以代碼的執行結果也毫無意外地和Java版本完全相同。
Read only Attempt to write Failed org.multiverse.api.exceptions.ReadonlyException: Can't open for write transactional object 'akka.stm.Ref@1761506447' because transaction 'DefaultTransaction' is readonly'
[1]近些年來,特別是隨著JVM上新語言的不斷湧現,由Kent Beck所著的《Smalltalk Best Practice Patterns》[Bec96]一書中所討論的一些設計模式又被重新發掘了出來。
文章轉自 並發編程網-ifeve.com
最後更新:2017-05-22 16:02:48