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

来源:互联网 发布:ssd优化工具 编辑:程序博客网 时间:2024/06/07 18:03
在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;};};@Overridepublic 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>>() {@Overridepublic 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
原创粉丝点击