《Java高并发程序设计》学习 --7.8 Actor的内置转换状态

45 篇文章 0 订阅
一个Actor内部消息处理函数可以拥有多个不同的状态,在特点的状态下,可以对同一消息进行不同的处理,状态之间也可以任意切换。
现在模拟一个婴儿Baby,假设婴儿会拥有两种不同的状态,开心或者生气。当带他玩的时候,他总是表现出开心状态,当让他睡觉时,他就会非常生气。
在这个简单的场景模拟中,会给这个婴儿Actor发送睡觉和玩两种指令。如果婴儿正在生气,还让他睡觉,他就会说“我已经生气了”,如果你让他玩,他就会变得开心。同样,如果他玩的正高兴,你让他继续玩,他就会说“我已经很愉快了”,如果让他睡觉,他马上变得生气。
下面的这个BabyActor模拟了上述场景:
public class BabyActor extends UntypedActor {
	
	private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
	
	public static enum Msg {
		SLEEP,PLAY,CLOSE;
	}
	
	Procedure<Object> angry = new Procedure<Object>() {
		@Override
		public void apply(Object message) throws Exception {
			System.out.println("angryApply:" + message);
			if(message == Msg.SLEEP) {
				getSender().tell("I am already angry", getSelf());
				System.out.println("I am already angry");
			} else if(message == Msg.PLAY) {
				System.out.println("I like playing");
				getContext().become(happy);
			}
		}
	};
	
	
	Procedure<Object> happy = new Procedure<Object>() {
		@Override
		public void apply(Object message) throws Exception {
			System.out.println("happyApply:" + message);
			if(message == Msg.PLAY) {
				getSender().tell("I am already happy :-)", getSelf());
				System.out.println("I am already happy :-)");
			} else if(message == Msg.SLEEP) {
				System.out.println("I don't want to sleep");
				getContext().become(angry);
			}
		}
	};
	
	@Override
	public void onReceive(Object msg) throws Exception {
		System.out.println("onReceive:" + msg);
		if(msg == Msg.SLEEP) {
			getContext().become(angry);
		} else if(msg == Msg.PLAY) {
			getContext().become(happy);
		} else {
			unhandled(msg);
		}
	}
}
上述代码中,使用了become()方法用于切换Actor的状态。方法become()接收一个Procedure参数。Procedure在这里表示一种Actor的状态,同时,更重要的是它封装了在这种状态下的消息处理逻辑。
在这个BabyActor既没有生气也不开心。因此angry处理函数和happy处理函数都不会工作。当BabyActor接收到消息时,系统会调用onReceive()方法来处理这个消息。
当onReceive()处理SLEEP消息时,它会切换当前Actor的状态为angry。如果是PLAY消息,则切换状态为happy。
一旦完成切换状态,当后续有新的消息送达时,就不会再由onReceive()函数处理了。由于angry和happy本身就是消息处理函数。因此,后续的消息就直接交由当前状态处理(angry或者happy),从而很好地封装了Actor的多个不同处理逻辑。
下面的代码向婴儿Actor发送了几条PLAY和SLEEP的消息:
ActorSystem system = ActorSystem.create("become", ConfigFactory.load("samplehello.conf"));
ActorRef child = system.actorOf(Props.create(BabyActor.class), "baby");
system.actorOf(Props.create(WatchActor_Router.class), "watcher");
child.tell(BabyActor.Msg.PLAY, ActorRef.noSender());
child.tell(BabyActor.Msg.SLEEP, ActorRef.noSender());
child.tell(BabyActor.Msg.PLAY, ActorRef.noSender());
child.tell(BabyActor.Msg.PLAY, ActorRef.noSender());
child.tell(PoisonPill.getInstance(), ActorRef.noSender());

其输出如下(进行过适量裁剪):
onReceive:PLAY
MyWorker is starting
happyApply:SLEEP
I don't want to sleep
angryApply:PLAY
I like playing
happyApply:PLAY
MyWorker is starting
MyWorker is starting
I am already happy :-)
可以看到,当地一个PLAY消息到来时,是由onReceive()函数进行处理的,在onReceive()中,将Actor切换为happy状态。因此,当SLEEP消息到达时,由happy.apply()函数处理,接着Actor切换为angry状态。当PLAY消息再次到达时,由angry.apply()函数处理。由此可见,Akka为Actor提供了灵活的状态切换机制,处于不同状态的Actor可以绑定不同的消息处理函数进行消息处理,这对构造结构化应用有着重要的帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值