Actor模型原理

1.Actor模型

在使用Java進行併發編程時須要特別的關注鎖和內存原子性等一系列線程問題,而Actor模型內部的狀態由它本身維護即它內部數據只能由它本身修改(經過消息傳遞來進行狀態修改),因此使用Actors模型進行併發編程能夠很好地避免這些問題,Actor由狀態(state)、行爲(Behavior)和郵箱(mailBox)三部分組成html

  1. 狀態(state):Actor中的狀態指的是Actor對象的變量信息,狀態由Actor本身管理,避免了併發環境下的鎖和內存原子性等問題
  2. 行爲(Behavior):行爲指定的是Actor中計算邏輯,經過Actor接收到消息來改變Actor的狀態
  3. 郵箱(mailBox):郵箱是Actor和Actor之間的通訊橋樑,郵箱內部經過FIFO消息隊列來存儲發送方Actor消息,接受方Actor從郵箱隊列中獲取消息

Actor的基礎就是消息傳遞

2.使用Actor模型的好處:

  1. 事件模型驅動--Actor之間的通訊是異步的,即便Actor在發送消息後也無需阻塞或者等待就可以處理其餘事情
  2. 強隔離性--Actor中的方法不能由外部直接調用,全部的一切都經過消息傳遞進行的,從而避免了Actor之間的數據共享,想要
    觀察到另外一個Actor的狀態變化只能經過消息傳遞進行詢問
  3. 位置透明--不管Actor地址是在本地仍是在遠程機上對於代碼來講都是同樣的
  4. 輕量性--Actor是很是輕量的計算單機,單個Actor僅佔400多字節,只需少許內存就能達到高併發

3.Actor模型原理

如下經過學生與教師之間的郵件通訊來理解akka中的Actor模型編程

學生-教師的消息傳遞

首先先只考慮學生單向發送消息給教師(學生--->教師),以下圖:
併發

圖解:app

  1. 學生建立一個ActorSystem
  2. 經過ActorSystem建立ActorRef,將QuoteRequest消息發送到ActorRef(教師代理)
  3. ActorRef(教師代理)消息傳遞到Dispatcher中
  4. Dispatcher依次的將消息發送到TeacherActor的郵箱中
  5. Dispatcher將郵箱推送到一條線程中
  6. 郵箱取出一條消息並委派給TeacherActor的receive方法

下面再詳細的解釋每一步驟框架

StudentSimulatorApp主程序詳解:

首先StudentSimulatorApp會先啓動JVM並初始化ActorSystem
dom

如上圖所示,StudentSimulatorApp的主要工做爲:異步

  1. 建立ActorSystem
    • ActorSystem做爲頂級Actor,能夠建立和中止Actors,甚至可關閉整個Actor環境,
      此外Actors是按層次劃分的,ActorSystem就比如Java中的Object對象,Scala中的Any,
      是全部Actors的根,當你經過ActorSystem的actof方法建立Actor時,實際就是在ActorSystem
      下建立了一個子Actor。
      可經過如下代碼來初始化ActorSystemide

      val system = ActorSystem("UniversityMessageSystem")高併發

  2. 經過ActorSystem建立TeacherActor的代理(ActorRef)
    • 看看TeacherActor的代理的建立代碼ui

      val teacherActorRef:ActorRef = system.actorOf(Props[TeacherActor])

    ActorSystem經過actorOf建立Actor,但其並不返回TeacherActor而是返
    回一個類型爲ActorRef的東西。
    ActorRef做爲Actor的代理,使得客戶端並不直接與Actor對話,這種Actor
    模型也是爲了不TeacherActor的自定義/私有方法或變量被直接訪問,所
    以你最好將消息發送給ActorRef,由它去傳遞給目標Actor
  3. 發送QuoteRequest消息到代理中
    • 你只需經過!方法將QuoteReques消息發送給ActorRef(注意:ActorRef也有個tell方法,其做用就委託回調給!)

      techerActorRef!QuoteRequest
      等價於teacherActorRef.tell(QuoteRequest, teacherActorRef)

完整StudentSimulatorApp代碼

object StudentSimulatorApp extends App{
 //初始化ActorSystem
 val actorSystem=ActorSystem("UniversityMessageSystem")
 //構建teacherActorRef
 val teacherActorRef=actorSystem.actorOf(Props[TeacherActor])
 //發送消息給TeacherActor
 teacherActorRef! QuoteRequest
 Thread.sleep (2000)
 //關閉 ActorSystem,若是不關閉JVM將不會退出
 actorSystem.shutdown()
}

QuoteRequest類

object TeacherProtocol{
 case class QuoteRequest() //請求
 case class QuoteResponse(quoteString:String) //響應
}

Dispatcher和MailBox

ActorRef將消息處理能力委派給Dispatcher,實際上,當咱們建立ActorSystem和ActorRef時,
Dispatcher和MailBox就已經被建立了

  • MailBox

    • 每一個Actor都有一個MailBox,一樣,Teacher也有個MailBox,其會檢查MailBox並處理消息。
      MailBox內部採用的是FIFO隊列來存儲消息,有一點不一樣的是,現實中咱們的最新郵件
      會在郵箱的最前面。
  • Dispatcher

    • Dispatcher從ActorRef中獲取消息並傳遞給MailBox,Dispatcher封裝了一個線程池,以後在
      線程池中執行MailBox。

      protected[akka] override def registerForExecution(mbox: Mailbox, ...): Boolean = {
        ...
       try {
       executorService execute mbox
       ...
      }

    • 爲何能執行MailBox?
      • 看看MailBox的實現,沒錯,其實現了Runnable接口

        private[akka] abstract class Mailbox(val messageQueue: MessageQueue) extends SystemMessageQueue with Runnable

TeacherActor

  • 當ActorRef發送消息調用目標Actor的reveive方法時,MailBox中的run方法被執行,接着從消息隊列中取出一條消息並傳遞給Actor處理

    class TeacherActor extends Actor {
     val quotes = List(
      "Moderation is for cowards",
      "Anything worth doing is worth overdoing",
      "The trouble is you think you have time",
      "You never gonna know if you never even try")
     def receive = {
      case QuoteRequest => {
      import util.Random
      //從list中隨機選出一條消息做爲迴應(這裏只print並沒迴應學生的請求)
      val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))
      println (quoteResponse)
      }
     }
    }
    TeacherActor的receive方法將匹配QuoteRequest消息

這裏有個國外博主寫的Akka系列博客都很贊,分享給你們!

本文參考資料

AKKA NOTES - ACTOR MESSAGING - 1
Akka框架——第一節:併發編程簡介
Akka Quickstart with Scala

相關文章
相關標籤/搜索