原文:https://doc.akka.io/docs/akka/2.5/general/jmm.htmlhtml
使用Lightbend平臺(包括Scala和Akka)的一個主要好處是它簡化了編寫併發軟件的過程。本文討論Lightbend平臺,特別是Akka如何在併發應用程序中使用共享內存。java
在Java 5以前,Java內存模型(JMM)定義不明確。當多個線程訪問共享內存時,有可能得到各類奇怪的結果,例如:安全
隨着Java 5中JSR 133的實現,不少這些問題都獲得瞭解決。JMM是一組基於「happens-before」關係的規則,它限制一個存儲器訪問必須在另外一個存儲器訪問以前發生。這些規則的兩個例子是:數據結構
儘管JMM看起來很複雜,但規範試圖在易用性和編寫高性能和可伸縮併發數據結構的能力之間找到平衡點。併發
使用Akka中的Actors實現,多個線程能夠經過兩種方式在共享內存上執行操做:app
爲了防止Actor的可見性和從新排序問題,Akka保證如下兩個「happens before」規則:post
這兩個規則僅適用於同一個actor實例,若是使用不一樣的actor,則無效。性能
若是您關閉引用,則還必須確保引用的實例是線程安全的。咱們強烈建議遠離使用鎖定的對象,由於它可能會致使性能問題,而且在最壞的狀況下會致使死鎖。這是同步的危險。this
因爲Akka在JVM上運行,所以仍然須要遵循一些規則。spa
關閉內部Actor狀態並將其暴露給其餘線程
import akka.actor.{ Actor, ActorRef }
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.collection.mutablecase class Message(msg: String)
class EchoActor extends Actor {
def receive = {
case msg ⇒ sender() ! msg
}
}class CleanUpActor extends Actor {
def receive = {
case set: mutable.Set[_] ⇒ set.clear()
}
}class MyActor(echoActor: ActorRef, cleanUpActor: ActorRef) extends Actor {
var state = ""
val mySet = mutable.Set[String]()def expensiveCalculation(actorRef: ActorRef): String = {
// this is a very costly operation
"Meaning of life is 42"
}def expensiveCalculation(): String = {
// this is a very costly operation
"Meaning of life is 42"
}def receive = {
case _ ⇒
implicit val ec = context.dispatcher
implicit val timeout = Timeout(5 seconds) // needed for `?` below// Example of incorrect approach
// Very bad: shared mutable state will cause your
// application to break in weird ways
Future { state = "This will race" }
((echoActor ? Message("With this other one")).mapTo[Message])
.foreach { received ⇒ state = received.msg }// Very bad: shared mutable object allows
// the other actor to mutate your own state,
// or worse, you might get weird race conditions
cleanUpActor ! mySet// Very bad: "sender" changes for every message,
// shared mutable state bug
Future { expensiveCalculation(sender()) }// Example of correct approach
// Completely safe: "self" is OK to close over
// and it's an ActorRef, which is thread-safe
Future { expensiveCalculation() } foreach { self ! _ }// Completely safe: we close over a fixed value
// and it's an ActorRef, which is thread-safe
val currentSender = sender()
Future { expensiveCalculation(currentSender) }
}
}
//#mutable-state
}
消息應該是不可變的,這是爲了不共享的可變狀態陷阱。
原文:https://doc.akka.io/docs/akka/2.5/general/jmm.html