Akka并发编程——第二节:Actor模型(一)
本節主要內容
1. 定義Actor
通過擴展akka.actor.Actor 特質并實現receive方法來定義Actor,代碼示例如下
//通過擴展Actor并實現receive方法來定義Actor class MyActor extends Actor {//獲取LoggingAdapter,用于日志輸出val log = Logging(context.system, this)//實現receive方法,定義Actor的行為邏輯,返回的是一個偏函數def receive = {case "test" => log.info("received test")case _ => log.info("received unknown message")}}receive方法被定義在Actor當中,方法標簽如下
//Actor中的receive方法定義, type Receive = PartialFunction[Any, Unit] def receive: Actor.Receive下面給出其完整使用代碼:
object Example_01 extends App{import akka.actor.Actorimport akka.event.Loggingimport akka.actor.ActorSystemimport akka.actor.Propsclass MyActor extends Actor {val log = Logging(context.system, this)def receive = {case "test" => log.info("received test")case _ => log.info("received unknown message")}}//創建ActorSystem對象val system = ActorSystem("MyActorSystem")//返回ActorSystem的LoggingAdpaterval systemLog=system.log//創建MyActor,指定actor名稱為myactorval myactor = system.actorOf(Props[MyActor], name = "myactor")systemLog.info("準備向myactor發送消息")//向myactor發送消息myactor!"test"myactor! 123//關閉ActorSystem,停止程序的運行system.shutdown() }代碼運行結果:
[INFO] [04/02/2016 09:29:54.223] [main] [ActorSystem(MyActorSystem)] 準備向myactor發送消息 [INFO] [04/02/2016 09:29:54.224] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received test [INFO] [04/02/2016 09:29:54.224] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received unknown message輸出“[INFO] [04/02/2016 09:29:54.224] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received test”中的[MyActorSystem-akka.actor.default-dispatcher-3]為對應的線程名,[akka://MyActorSystem/user/myactor]為Actor路徑信息, received test為
def receive = {case "test" => log.info("received test")case _ => log.info("received unknown message")}方法處理后的輸出。關于[akka://MyActorSystem/user/myactor]路徑信息,將在后續內容中進行詳細闡述。
也可以通過混入ActorLogging來實現日志功能,具體代碼如下:
class MyActor extends Actor with ActorLogging{def receive = {case "test" => log.info("received test")case _ => log.info("received unknown message")}}ActorLogging的定義如下:
trait ActorLogging { this: Actor ?private var _log: LoggingAdapter = _def log: LoggingAdapter = {// only used in Actor, i.e. thread safeif (_log eq null)_log = akka.event.Logging(context.system, this)_log}}完整代碼如下:
/**定義Actor時混入ActorLogging*/ object Example_02 extends App{import akka.actor.Actorimport akka.actor.ActorSystemimport akka.actor.Propsclass MyActor extends Actor with ActorLogging{def receive = {case "test" => log.info("received test")case _ => log.info("received unknown message")}}//創建ActorSystem對象val system = ActorSystem("MyActorSystem")//返回ActorSystem的LoggingAdpaterval systemLog=system.log//創建MyActor,指定actor名稱為myactorval myactor = system.actorOf(Props[MyActor], name = "myactor")systemLog.info("準備向myactor發送消息")//向myactor發送消息myactor!"test"myactor! 123//關閉ActorSystem,停止程序的運行system.shutdown() }代碼運行結果:
[INFO] [04/02/2016 09:39:21.088] [main] [ActorSystem(MyActorSystem)] 準備向myactor發送消息 [INFO] [04/02/2016 09:39:21.089] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received test [INFO] [04/02/2016 09:39:21.089] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/myactor] received unknown message代碼原理與Example_01類似,這里不再贅述。
2. 創建Actor
在前面兩個例子中,通過
val myactor = system.actorOf(Props[MyActor], name = "myactor")創建Actor,需要注意的是system.actorOf方法返回的是ActorRef對象,ActorRef為Actor的引用,使用ActorRef對象可以進行消息的發送等操作。Props為配置對象,在創建Actor時使用,它是不可變的對象,因此它是線程案例且完全可共享的。Akka中創建Actor時,也允許直接傳入MyActor對象的引用,例如
//直接通過new MyActor的方式傳入MyActor對象的引用,注意這里是Props(new MyActor) val myactor = system.actorOf(Props(new MyActor), name = "myactor")但是Akka不推薦這么做,官方文檔給出的解釋是這種方式會導致不可序列化的Props對象且可能會導致競爭條件(破壞Actor的封裝性)。另外需要特別注意的是,不允許通過下列代碼創建Actor
//下列兩行代碼編譯可以通過,但運行時出拋出異常val myActor=new MyActorval myactor = system.actorOf(Props(myActor), name = "myactor")完整運行代碼如下:
/**創建Actor*/ object Example_03 extends App{import akka.actor.Actorimport akka.actor.ActorSystemimport akka.actor.Propsclass MyActor extends Actor with ActorLogging{def receive = {case "test" => log.info("received test")case _ => log.info("received unknown message")}}val system = ActorSystem("MyActorSystem")val systemLog=system.log//下列兩行代碼編譯可以通過,但運行時出拋出異常val myActor=new MyActorval myactor = system.actorOf(Props(myActor), name = "myactor")systemLog.info("準備向myactor發送消息")//向myactor發送消息myactor!"test"myactor! 123//關閉ActorSystem,停止程序的運行system.shutdown() }運行結果如下:
Exception in thread "main" akka.actor.ActorInitializationException: You cannot create an instance of [chapter02.Example_03$MyActor] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.at akka.actor.ActorInitializationException$.apply(Actor.scala:167)at akka.actor.Actor$class.$init$(Actor.scala:423)at chapter02.Example_03$MyActor.<init>(MyActor.scala:73)at chapter02.Example_03$delayedInit$body.apply(MyActor.scala:84)at scala.Function0$class.apply$mcV$sp(Function0.scala:40)at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)at scala.App$$anonfun$main$1.apply(App.scala:71)at scala.App$$anonfun$main$1.apply(App.scala:71)at scala.collection.immutable.List.foreach(List.scala:318)at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)at scala.App$class.main(App.scala:71)at chapter02.Example_03$.main(MyActor.scala:68)at chapter02.Example_03.main(MyActor.scala)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:606)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)從“You cannot create an instance of [chapter02.Example_03$MyActor] explicitly using the constructor (new). You have to use one of the ‘actorOf’ factory methods to create a new actor.”可以看到,不能通過顯式地調用構造函數創建Actor,只能使用actorOf工廠方法創建Actor。
下面介紹2種在實際中經常使用的Actor創建方法
(1)調用system.actorOf創建Actor
完整代碼在Example_01、Example_02中已經演示過了,這里需要說明的是通過system.actorOf工廠方法創建的Actor為頂級Actor
在Akka框架中,每個Akka應用程序都會有一個守衛Actor,名稱為user,所有通過system.actorOf工廠方法創建的Actor都為user的子Actor,也是整個Akka程序的頂級Actor。
(2)調用context.actorOf創建Actor
完整代碼如下:
- 1
代碼運行結果
[INFO] [04/02/2016 15:05:34.770] [main] [ActorSystem(MyActorSystem)] 準備向myactor發送消息 [INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor/myChild] received test [INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received test [INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-2] [akka://MyActorSystem/user/firstActor] received 123 [INFO] [04/02/2016 15:05:34.771] [MyActorSystem-akka.actor.default-dispatcher-3] [akka://MyActorSystem/user/firstActor/myChild] received unknown message通過代碼的運行結果可以看到,FirstActor的Actor路徑信息為akka://MyActorSystem/user/firstActor,而通過
class FirstActor extends Actor with ActorLogging{//通過context.actorOf方法創建Actorval child = context.actorOf(Props[MyActor], name = "myChild")def receive = {case x => child ! x;log.info("received "+x)}}代碼使用context.actorOf創建的MyActor,其Actor路徑信息為[akka://MyActorSystem/user/firstActor/myChild],這意味著mychild為firstActor的子Actor,層次結構如下圖所示
也就是說context.actorOf和system.actorOf的差別是system.actorOf創建的actor為頂級Actor,而context.actorOf方法創建的actor為調用該方法的Actor的子Actor
總結
以上是生活随笔為你收集整理的Akka并发编程——第二节:Actor模型(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TensorFlow学习笔记(一)安装、
- 下一篇: Akka并发编程——第三节:Actor模