使用Lightbend平臺(tái)的主要好處,包括Scala和Akka,它簡(jiǎn)化了編寫并發(fā)軟件的過程烦周。本文討論如何Lightbend平臺(tái),尤其是Akka,并發(fā)應(yīng)用程序共享內(nèi)存的方法。
Java內(nèi)存模型
在Java 5之前,Java內(nèi)存模型(JMM)有誤的定義睦授。由多個(gè)線程訪問共享內(nèi)存時(shí)可以得到各種奇怪的結(jié)果锈锤,如:
- 線程看不到其它線程寫入的值:可見性問題;
- 線程觀察其他線程的“不可能”行為肆氓,指令沒有按預(yù)期的順序執(zhí)行引起:指令重新排序的問題袍祖。
Java 5的JSR 133解決了這些問題。JMM的一組規(guī)則是基于“發(fā)生前”的關(guān)系谢揪,這強(qiáng)制了當(dāng)一個(gè)內(nèi)存訪問發(fā)生在其它之前蕉陋,相反地,允許在出現(xiàn)故障時(shí)發(fā)生拨扶。兩個(gè)例子說明這些規(guī)則: - 監(jiān)視鎖規(guī)則:釋放一個(gè)鎖之前每一個(gè)后繼得到相同的鎖凳鬓。
- 易失變量規(guī)則:寫易失性變量之前每一個(gè)后續(xù)的讀相同的易失性變量。
盡管JMM看起來復(fù)雜患民,規(guī)范試圖找到一個(gè)易于使用和寫的能力之間的平衡性能缩举,可伸縮的并發(fā)數(shù)據(jù)結(jié)構(gòu)。
Actor和Java內(nèi)存模型
Akka中Actor的實(shí)現(xiàn),有兩種方法在共享內(nèi)存環(huán)境下執(zhí)行多線程行為:
- 如果一個(gè)消息發(fā)送到一個(gè)Actor(如由另一個(gè)Actor)仅孩。在大多數(shù)情況下托猩,消息是不可變的,但是如果這個(gè)信息構(gòu)造不可變的對(duì)象是不正確的辽慕,沒有“發(fā)生前”的規(guī)則京腥,接收者可能部分初始化數(shù)據(jù)結(jié)構(gòu),甚至可能值是憑空捏造的(長(zhǎng)型/雙精度型)
- 如果Actor在處理消息時(shí)改變內(nèi)部狀態(tài)溅蛉,并在片刻后訪問這個(gè)狀態(tài)在處理另一個(gè)消息時(shí)公浪。深刻認(rèn)識(shí)到Actor模型中并不保證,同樣的線程會(huì)對(duì)不同消息執(zhí)行相同的Actor温艇。
為了避免Actor的可見性和重排序問題因悲,Akka保證了下例兩個(gè)“發(fā)生前”的規(guī)則: - Actor發(fā)送規(guī)則:一個(gè)Actor發(fā)送消息發(fā)生之前由通一個(gè)Actor接收消息。
- Actor后續(xù)處理規(guī)則:在處理消息發(fā)生前由同一個(gè)Actor處理后續(xù)消息勺爱。
注意
通俗的講改變Actor的內(nèi)部字段在下一個(gè)消息的可見晃琳。Actor中的字段不能是易失或相價(jià)的。
兩個(gè)規(guī)則僅適用于同一個(gè)Actor實(shí)例琐鲁,對(duì)不同的Actor無效卫旱。
Futures和Java模型
完成Feature的“發(fā)生前”執(zhí)行調(diào)用的回調(diào)注冊(cè)。
我們建議不要封閉非final字段(在Java用final围段,在Scala中用val )如果你選擇封閉非final字段顾翼,它們必須被標(biāo)識(shí)為‘volatile’為了字段的當(dāng)前值是可見的回調(diào)。
如果封閉一個(gè)引用奈泪,需要確保實(shí)例是線程安全的适贸。我們強(qiáng)烈建議遠(yuǎn)離使用鎖定的對(duì)象,因?yàn)樗谧顗牡那闆r下涝桅,引入性能問題和死鎖拜姿。這樣的同步是危險(xiǎn)的。
Actor和共享可變狀態(tài)
由于Akka運(yùn)行在JVM上仍有一些規(guī)則要遵循冯遂。
- 封閉Actor內(nèi)部狀態(tài)蕊肥,并向其它線程暴露它。
1. class MyActor extends Actor {
2. var state = ...
3. def receive = {
4. case _ =>
5. //Wrongs
6.
7. // Very bad, shared mutable state,
8. // will break your application in weird ways
9. Future { state = NewState }
10. anotherActor ? message onSuccess { r => state = r }
11.
12. // Very bad, "sender" changes for every message,
13. // shared mutable state bug
14. Future { expensiveCalculation(sender()) }
15.
16. //Rights
17.
18. // Completely safe, "self" is OK to close over
19. // and it's an ActorRef, which is thread-safe
20. Future { expensiveCalculation() } onComplete { f => self ! f.value.get }
21.
22. // Completely safe, we close over a fixed value
23. // and it's an ActorRef, which is thread-safe
24. val currentSender = sender()
25. Future { expensiveCalculation(currentSender) }
26. }
27.}
- 消息應(yīng)該是不可變的,這是為了避免共享可變狀態(tài)的陷阱蛤肌。