天天看點

軟體事務記憶體導論(三)用Akka/Multiverse STM實作并發

上面我們已經學習了如何在clojure裡使用stm,我猜你現在一定很好奇如何在java代碼中使用stm。而對于這一需求,我們有如下選擇:

直接在java中使用clojure stm。方法非常簡單,我們隻需将事務的代碼封裝在一個callable接口的實作中就行了,詳情請參見第7章。

喜歡用注解(annotation)的開發者可能會更傾向于使用multiverse的stm api.

除了stm之外,如果我們計劃使用角色(actor),那麼還可以考慮選擇akka庫。

multiverse是由peter

veentjer主持開發的一個基于java的stm實作。通過這個庫,我們可以在java代碼中使用注解來辨別事務邊界。我們既可以用

@transactionalmethod注解将單個的方法标記為事務性的,也可以用@transactionalobject注解将一個類的所有方法都

标記為事務性的。為了與其他jvm上的語言進行內建,multiverse還提供了一組豐富的api來控制事物的開始和結束。

akka是一個由jonas

boner主持開發的一個基于scala的解決方案,該方案可以用于包括java在内的很多其他運作于jvm上的語言。akka不但提供了stm和基于角

色(actor)的并發方案,還提供了将二者混合使用的選項。此外,akka使用multiverse作為其stm的實作并提供了aci(acid的子

集)特性。

akka的性能非常棒,并且由于它既支援stm又支援基于角色(actor)的模型(詳情請參見第8章),本章我們将會用它來實作示範java stm的例子。

<b>akka/multiverse中的事務</b>

akka的java版采用了multiverse的clojure風格的stm。與java那繁冗的代碼風格相比,clojure風格的akka不

會強迫我們在能夠修改可變實體之前就建立事務。如果我們沒有主動提供事務,則akka/multiverse就會自動把通路請求封裝在一個事務中。是以當

我們處于事務之中時,akka的ref與clojure的ref的表現是相同的;而當我們位于事務之外時,akka

ref的表現則更像是clojure的atom。換句話說,想要使變更同步且有序就必須使其在事務中完成,否則變更将是同步但無序的。在任何情況

下,akka都會保證對于ref的更改是原子的、隔離的且一緻的,并同時提供了不同等級的協調粒度。

在akka中,我們既可以用寫代碼的方式在事務層對事務進行配置,也可以通過配置檔案在應用程式/jvm層進行配置。例如,我們可以将一個事務定義

為隻讀(readonly),于是akka将不再允許任何位于該事務範圍内的akka引用被修改。這樣做的好處是,如果我們将一些不可變的事務設定為隻

讀,則程式性能将會得到一定的提升。除此之外,我們還可以控制在沖突情況下事務的最大重試次數。當然,還有很多其他參數可供我們配置,詳情請參閱akka

的幫助文檔。

akka擴充了multiverse中的嵌套事務(請參見6.9節),是以我們能夠很友善地在事務中調用啟動其他事務的函數。預設情況下,這些内部事務或嵌套事務都是與其外部事務融為一體的。

<b>使用akka引用和事務</b>

clojure中的ref是在語言層定義的,而

akka是一個公共類庫是以不能依賴任何現有語言的支援。是以akka在其akka.stm包中提供了一個托管事務引用(managed

transactional

reference)ref和一些為原始類型而設的特殊類,如intref、longref等。ref(以及所有原始類型的特化引用)代表指向類型t的一

個不可變值的托管可變實體(managed mutable

identity)。像integer、long、double、string這些類型以及其他不可變類型都符合作為值對象的(value

object)條件。如果我們用了自己定義的類,則必須保證這個類是不可變的。也就是說,這個自定義的類隻能包含final字段。

我們可以建立一個ref的執行個體作為托管事務引用,其值可以在初始化時指定或幹脆不指定(預設為null)。如果想獲得引用的目前值,可以使用

get()函數。如果要使引用指向另一個可變實體,則可以使用swap()函數。這些調用可以在我們提供的事務裡執行,但如果我們沒提供事務的話,它們也

可以在其各自的事務中運作。

當多個線程都試圖更改同一個托管引用時,akka可以保證隻有一個變更可以寫入記憶體而其他變更将全部重做。akka有專門的事務工具負責管理事務跨

越記憶體栅欄的過程。也就是說,akka(通過multiverse)保證了在事務中一個托管ref變更的送出會先于後續所有其他事務對該ref的讀操作,

即該變更對所有其他事務可見。