《Java高并发程序设计》学习 --7.10 多个Actor同时修改数据:Agent

在Actor的编程模型中,Actor之间主要通过消息进行信息传递。因此,很少发生多个Actor需要访问一个共享变量的情况。但在实际开发中,这种情况很难完全避免。如果多个Actor需要对同一个共享变量进行读写时,如何保证线程安全呢?
在Akka中,使用一种叫做Agent的组件来实现这个功能。一个Agent提供了对一个变量的异步更新。当一个Actor希望改变Agent的值时,它会向这个Agent下发一个动作(action)。当多个Actor同时改变Agent时,这些action将会在ExecutionContext中被并发调度执行。在任何时刻,一个Agent最多只能执行一个action,对于某一个线程来说,它执行action的顺序与它的发生顺序一致,但对于不同线程来说,这些action可能会交织在一起。
Agent的修改可以使用两个方法send()或者alter()。它们都可以向Agent发送一个修改动作。但是send()方法没有返回值,而alter()方法会返回一个Future对象便于跟踪Agent的执行。
下面模拟一个场景:有10个Actor,它们一起对一个Agent执行累加操作,每个agent累加10000次,如果没有意外,那么agent最终的值将是100000,如果Actor间的调度出现问题,那么这个值可能小于100000。
public class CounterActor extends UntypedActor {
	Mapper<Integer, Integer> addMapper = new Mapper<Integer, Integer>() {
		public Integer apply(Integer i) {
			return i + 1;
		};
	};
	@Override
	public void onReceive(Object msg) throws Exception {
		if(msg instanceof Integer) {
			for(int i=0; i<10000; i++) {
				Future<Integer> f = AgentDemo.counterAgent.alter(addMapper);
				AgentDemo.futures.add(f);
			}
			getContext().stop(getSelf());
		} else 
			unhandled(msg);
	}
}
上述代码定义了一个累加的Actor:CounterActor,且定义了累计动作 action addMapper。它的作用是对Agent的值进行修改。
CounterActor的消息处理函数onReceive()中,对全局counterAgent进行累加操作,alter()指定了累加动作addMapper。由于我们希望在将来知道累加行为是否完成,因此在这里将返回Future对象进行收集。完成任务后,Actor自行退出。
程序的主函数如下:
public class AgentDemo {
	public static Agent<Integer> counterAgent = Agent.create(0, ExecutionContexts.global());
	static ConcurrentLinkedDeque<Future<Integer>> futures = new ConcurrentLinkedDeque<Future<Integer>>();

	public static void main(String[] args) {
		final ActorSystem system = ActorSystem.create("agentdemo", ConfigFactory.load("samplehello.conf"));
		ActorRef[] counter = new ActorRef[10];
		for(int i=0; i<counter.length; i++) {
			counter[i] = system.actorOf(Props.create(CounterActor.class),"counter_" + i);
		}
		final Inbox inbox = Inbox.create(system);
		for(int i=0; i<counter.length; i++) {
			inbox.send(counter[i], 1);
			inbox.watch(counter[i]);
		}

		int closeCount = 0;
                //等待所有Actor全部结束
		while(true) {
			Object msg = inbox.receive(Duration.create(1, TimeUnit.SECONDS));
			if(msg instanceof Terminated) {
				closeCount++;
				if(closeCount == counter.length) {
					break;
				}
			} else {
				System.out.println(msg);
			}
		}
		//等待所有的累加线程完成,因为他们都是异步的
		Futures.sequence(futures, system.dispatcher()).onComplete(
				new OnComplete<Iterable<Integer>>() {
					@Override
					public void onComplete(Throwable arg0,
							Iterable<Integer> arg1) throws Throwable {
						System.out.println("counterAgent=" + counterAgent.get());
						system.shutdown();
					}
				}, system.dispatcher());
	}
}
上述代码中,创建了10个CounterActor对象。使用Inbox与CounterActor进行通信。第14行的消息将触发CounterActor进行累加操作。第20~30行系统将等待所有10个CounterActor运行结束。执行完成后,我们便已经收集了所有的Future。在第32行,将所有的Future进行串行组合(使用sequence()方法),构造了一个整体的Future,并为它创建onComplete()回调函数。在所有的Agent操作执行完成后,onComplete()方法就会被调用(第35行)。在这个例子中,简单地输出最终的counterAgent值,并关闭系统。
执行上述程序,得到结果:
counterAgent=100000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值