方法4:雇用一个Actor
  Actor模型是对我们本文中所探讨的方法的一个奇怪的补充。JDK中没有actor的实现;因此你必须引用一些实现了actor的库。
  简短地说,在actor模型中,你把一切都看做是一个actor。一个actor是一个计算实体,像上面第一个例子中的线程,它可以从其他actor那里接收消息,因为一切都是actor。
  在应答消息时,它可以给其他actor发送消息,或者创建新的actor并与之交互,或者只改变自己的内部状态。
  相当简单,但这是一个非常强大的概念。生命周期和消息传递由你的框架来管理,你只需要指定计算单元是什么可以了。另外,actor模型强调避免全局状态,这会带来很多便利。你可以应用监督策略,例如免费重试,更简单的分布式系统设计,错误容忍度等等。
  下面是一个使用Akka Actors的例子。Akka Actors有Java接口,是流行的JVM Actor库之一。实际上,它也有Scala接口,并且是Scala目前默认的actor库。Scala曾经在内部实现了actor。不少JVM语言都实现了actor,比如Fantom。这些说明了Actor模型已经被广泛接受,并被看做是对语言非常有价值的补充。

static class Message {
String url;
Message(String url) {this.url = url;}
  }
  static class Result {
  String html;
  Result(String html) {this.html = html;}
  }
  static class UrlFetcher extends UntypedActor {
  @Override
  public void onReceive(Object message) throws Exception {
  if (message instanceof Message) {
  Message work = (Message) message;
  String result = WS.url(work.url).get();
  getSender().tell(new Result(result), getSelf());
  } else {
  unhandled(message);
  }
  }
  }
  static class Querier extends UntypedActor {
  private String question;
  private List<String> engines;
  private AtomicReference<String> result;
  public Querier(String question, List<String> engines, AtomicReference<String> result) {
  this.question = question;
  this.engines = engines;
  this.result = result;
  }
  @Override public void onReceive(Object message) throws Exception {
  if(message instanceof Result) {
  result.compareAndSet(null, ((Result) message).html);
  getContext().stop(self());
  }
  else {
  for(String base: engines) {
  String url = base + question;
  ActorRef fetcher = this.getContext().actorOf(Props.create(UrlFetcher.class), "fetcher-"+base.hashCode());
  Message m = new Message(url);
  fetcher.tell(m, self());
  }
  }
  }
  }
  private static String getFirstResultActors(String question, List<String> engines) {
  ActorSystem system = ActorSystem.create("Search");
  AtomicReference<String> result = new AtomicReference<>();
  final ActorRef q = system.actorOf(
  Props.create((UntypedActorFactory) () -> new Querier(question, engines, result)), "master");
  q.tell(new Object(), ActorRef.noSender());
  while(result.get() == null);
  return result.get();
  }

  Akka actor在内部使用ForkJoin框架来处理工作。这里的代码很冗长。不要担心。大部分代码是消息类Message和Result的定义,然后是两个不同的actor:Querier用来组织所有的搜索引擎,而URLFetcher用来从给定的URL获取结果。这里代码行比较多是因为我不愿意把很多东西写在同一行上。Actor模型的强大之处来自于Props对象的接口,通过接口我们可以为actor定义特定的选择模式,定制的邮箱地址等。结果系统也是可配置的,只包含了很少的活动件。这是一个很好的迹象!
  使用Actor模型的一个劣势是,它要求你避免全局状态,因此你必须小心的设计你的应用程序,而这可能会使项目迁移变得很复杂。同时,它也有不少优点,因此学习一些新的范例和使用新的库是完全值得的。
  反馈时间:你使用什么?
  你常用的并发方式是什么?你理解它背后的计算模式是什么吗?仅仅使用一个包含Job或者后台任务对象的框架来自动地为你的代码添加异步计算能力?
  为了收集更多信息,以找出我是否应该继续更深入地讲解一些不同的并发模式,例如,写一篇关于Akka如何工作,以及它Java接口的优点和缺点,我创建了一个简单的调查。亲爱的读者,请填一下调查表。我非常感谢你的互动!
  总结
  这篇文章中我们讨论了在Java应用中添加并行的几种不同方法。从我们自己管理Java线程开始,我们逐渐地发现更高级的解决方案,执行不同的executor服务、ForkJoin框架和actor计算模型。
  不知道当你面临真实问题时该如何选择?它们都有各自的优缺点,你需要在直观和易用性、配置和增加/减少机器性能等方面做出选择。