并发编程模型Akka

Akka 简介

Akka 介绍

  • Akka 是编写并发程序的框架,它提供了一种称为Actor的并发模型,其粒度比线程更小,你可以在系统中启用大量的Actor。
  • Akka用Scala语言编写,同时提供了Scala和Java的开发接口。
  • Akka的出现使得程序员不再过多的考虑线程、锁和资源竞争的问题,可以轻松的写出高效稳定的并发程序。

Akka 用于解决什么问题

单线程模型中,不会出现数据一致性的问题,但是系统的性能不能保证。当程序是多线程并发处理数据时需要保证数据的一致性和正确性,需要对代码加入同步条件synchronized,这样对程序效率就有很大的影响。
Akka是一种处理并发问题的解决方案,它的出现解决了这个问题,简化并发编程,提升程序性能。

Actor模型

Akka基于Actor模型,提供了一个用于构建可扩展的(Scalable)、弹性的(Resilient)、快速响应的(Responsive)应用程序的平台。
在这里插入图片描述

  • 在基于Actor的系统里,所有的事物都是Actor,Actor于Actor之间只能通过消息通信,如图的信封。当Actor给另一个Actor发送消息时,消息是有顺序的,只需要将消息投递到相应的邮箱即可。
  • 怎么处理消息是由接受消息的Actor决定,发送消息的Actor既可以等待回复,也可以异步处理。
  • ActorSystem用来创建并管理Actor,ActorSystem可以理解为一个工厂,专门用来创建Actor,ActorSystem是单例的,一个JVM进程中只有一个即可,但是Actor可以有多个。
  • Actor模型是轻量级的事件处理(1GB内存可以容纳百万个Actor),因此性能恒高。

Actor模型工作机制

在这里插入图片描述

  1. A actor和B actor需要继承Actor,并重写receive方法
  2. 通过ActorSystem创建actorfactory
  3. 通过actorfactory创建A ActorRef和B ActorRef
  4. ActorRef 可以理解成Actor的代理或者引用。消息通过ActorRef来发送,而不是通过Actor来发送,往哪个Actor发送消息需要先拿到对应的ActorRef才可以发送消息。例如上图A actor 向 B actor发送消息,那么A actor需要拿到B actor的ActorRef才可以向B actor发送消息。
  5. 发送消息到Dispatcher Message(消息分发器),它得到消息后会将消息发送到对应的MailBox中。其中MailBox可以理解成消息队列,能够缓冲多个消息,遵守FIFO原则。
  6. Actor 通过receive方法来获取消息,然后进行处理。
  7. 如图如果B actor接受到A actor的消息后回复A actor 需要通过sender()方法来获取到A actor的ActorRef,然后就可以回复A actor的消息。
  8. 每一个消息其实就是一个Message对象。Message继承了Runnable。

Actor应用实例

Maven依赖

<!-- 定义一下常量 -->
    <properties>
        <encoding>UTF-8</encoding>
        <scala.version>2.11.8</scala.version>
        <scala.compat.version>2.11</scala.compat.version>
        <akka.version>2.4.17</akka.version>
    </properties>
 	<dependencies>
        <!-- 添加scala的依赖 -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <!-- 添加akka的actor依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>

        <!-- 多进程之间的Actor通信 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>
    </dependencies>

单Actor通信案例

单个 Actor 如何使用案例

import akka.actor.{Actor, ActorRef, ActorSystem, Props}
//说明
//1. 当我们继承Actor后,就是一个Actor,核心方法receive 方法重写
class SayHelloActor extends Actor{
  //说明
  //1. receive方法,会被该Actor的MailBox(实现了Runnable接口)调用
  //2. 当该Actor的MailBox 接收到消息,就会调用 receive
  //3. type Receive = PartialFunction[Any, Unit]
  override def receive:Receive = {
    case "hello" => println("收到hello, 回应hello too:)")
    case "ok" => println("收到ok, 回应ok too:)")
    case "exit" => {
      println("接收到exit指令,退出系统")
      context.stop(self) //停止actoref
      context.system.terminate()//退出actorsystem
    }
    case _ => println("匹配不到")
  }
}

object SayHelloActorDemo {

  //1. 先创建一个ActorSystem, 专门用于创建Actor
  private val actoryFactory = ActorSystem("actoryFactory")
  //2. 创建一个Actor的同时,返回Actor的ActorRef
  //   说明
  //(1) Props[SayHelloActor] 创建了一个 SayHelloActor实例,使用反射
  //(2) "sayHelloActor" 给actor取名
  //(3) sayHelloActorRef: ActorRef 就是 Props[SayHelloActor] 的ActorRef
  //(4) 创建的SayHelloActor 实例被ActorSystme接管
  private val sayHelloActorRef: ActorRef = actoryFactory.actorOf(Props[SayHelloActor],"sayHelloActor")

  def main(args: Array[String]): Unit = {

    //给SayHelloActor 发消息(邮箱)
    sayHelloActorRef ! "hello"
    sayHelloActorRef ! "ok"
    sayHelloActorRef ! "ok~"
    //研究异步如何退出ActorSystem
    sayHelloActorRef ! "exit"

  }

}

多actor通信案例

两个actor之间如何通信案例。具体通信过程参照注释中的序号。

  • A actor

import akka.actor.{Actor, ActorRef}

class AActor(actorRef: ActorRef) extends Actor {

  val bActorRef: ActorRef = actorRef

  override def receive: Receive = {
    //(1)从 start 开始,使用Scala的模式匹配
    case "start" => {
      println("AActor(黄飞鸿) 出招了 , 见招")
      self ! "我打" //发给自己
    }
    //(2)
    case "我打" => {
      //给BActor 发出消息
      //这里需要持有BActor的引用(BActorRef)
      println("AActor(黄飞鸿) : 看我佛山无影脚")
      Thread.sleep(1000)
      bActorRef ! "我打" //给BActor 发出消息
    }
    //(4)
    case "不要打我了" => {
      //给BActor 发出消息
      //这里需要持有BActor的引用(BActorRef)
      println("AActor(黄飞鸿) :乔峰你太厉害了 放过我")
      Thread.sleep(1000)
      bActorRef ! "放过我" //给BActor 发出消息
    }

  }
}

  • B actor
import akka.actor.Actor

class BActor extends Actor{
  override def receive:Receive = {
  //(3)
    case "我打" => {
      println("BActor(乔峰) :挺猛 看我降龙十八掌")
      Thread.sleep(1000)
      //通过sender() 可以获取到发送消息的actor的ref,也就是AActor的引用
      sender() ! "不要打我了"
    }
     //(5)
    case "放过我" => {
      println("BActor(乔峰) :那就放过你吧")
      Thread.sleep(1000)
      context.stop(self) //停止BActor的ActorRef
      //通过sender() 可以获取到发送消息的actor的ref,也就是AActor的引用
      context.stop(sender())//停止AActor的ActorRef
      context.system.terminate()//退出actorsystem
      println("回合结束")
    }
  }
}
  • 主函数
import akka.actor.{ActorRef, ActorSystem, Props}

object ActorGame extends App {
  //创建ActorSystem
  val actorfactory = ActorSystem("actorfactory")
  //先创建BActor引用/代理
  val bActorRef: ActorRef = actorfactory.actorOf(Props[BActor], "bActor")
  //创建AActor的引用,并将bActorRef给到aActorRef --》(Props(new AActor(bActorRef)))
  val aActorRef: ActorRef = actorfactory.actorOf(Props(new AActor(bActorRef)), "aActor")

  //aActor出招 消息发送给A actor
  aActorRef ! "start"
}

  • 结果

在这里插入图片描述
深入了解Akka并发编程模型,请点击下面链接:
链接1: Akka网络编程案例.
链接2: Akka模拟Spark Master和Worker通信.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值