1.并发编程模型Akka
1.1. Akka 介绍
多线程开发要处理并发,锁,线程同步等一系列问题,一不小心,弄个大的bug,所以一般都不愿意自己动手写,能不能换一种思路,可以不可以搞一种更高级的抽象模型,让我想实现多线程应用的时候,不用再考虑这些底层问题呢?
Akka是JVM平台上构建高并发、分布式和容错应用的工具包和运行平台。Akka是用 Scala语言编程的一个并发编程框架,该框架基于Actor编程模型。
Akka实现并发,基于Actor模式,
相当于Java实现并发,基于Thread。
1.2.Akka中Actor模型
与java的基于共享数据和锁的线程模型不同,scala的actor包则提供了另外一种不共享任何数据、依赖消息传递的模型。
在基于Actor的系统里,所有的事物都是Actor,就好像在面向对象设计里面所有的事物都是对象一样。但是有一个重要区别,那就是Actor模型是作为一个并发模型设计和架构的,而面向对象模式则不是。Actor与Actor之间只能通过消息通信。
可以将Actor当作是一群人,他们互相之间不会面对面地交流,而只是通过邮件的方式进行沟通。传递消息是actor模型的基础。
Actor是一种编程模型,通过发送消息的方式实现并发编程
必须有多个相同业务逻辑的Actor才可以实现并发编程,一个Actor相当于一个实例(老师/学生),消息是发送的数据(邮件)
Actor和Actor之间可以相互发送消息
每一个Actor中都有相应的业务逻辑,如果使用编程,定义一个类,在类中定义方法,通过new一个实例,即可得到一个Actor
Actor是ActorSystem创建的,ActorSystem的职责是负责创建并管理自己创建的Actor,ActorSystem的单例的,一个JVM进程中有一个即可,而Acotr是多例的。ActorSystem相当于老大,Actor相当于干活的小弟
1.3.使用Actor模型的好处:
事件模型驱动--Actor之间的通信是异步的,即使Actor在发送消息后也无需阻塞或者等待就能够处理其他事情;
强隔离性--Actor中的方法不能由外部直接调用,所有的一切都通过消息传递进行的,从而避免了Actor之间的数据共享,想要观察到另一个Actor的状态变化只能通过消息传递进行询问;
轻量性--Actor是非常轻量的计算单机,单个Actor仅占400多字节,只需少量内存就能达到高并发;
Java内置线程模型 Scala actor模型
“共享数据-锁”模型(share data and lock) share nothing
每个object有一个monitor,监视多线程对共享数据的访问 不共享数据,actor之间通过message通信
加锁的代码段用synchronized标识
死锁问题
每个线程内部是顺序执行的 每个actor内部是顺序执行的
1.4.maven工程:
如果本地仓库的jar包没有下载成功,就有一个.lastUpdate为后缀的文件,删除该文件,然后重新下载。
1.4.1.IDEA中配置maven
默认加载本机的maven目录,所以正常不用配置
1.4.2.创建maven工程
1.4.3.Pom依赖:
完整参考pom.xml文件
<!-- 定义一下常量 -->
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<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>
配置:
如果按照外部的maven
1,解压安装包
2,修改环境变量, M2_HOME= 把%M2_HOME%\bin 添加到Path中。
3,在cmd窗口中,使用mvn -version 里查看配置的环境变量是否生效
如果把settging.xml文件添加到了 用户/.m2目录下,
可以修改本地仓库。
修改为自己指定的目录即可。
<localRepository>/path/to/local/repo</localRepository>
本地仓库:
作用:
存放maven工程下载的依赖jar包。
当我们配置好了一个依赖jar包之后:
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
maven就会首先去本地仓库中找,没找到就去找镜像(远程仓库),如果该远程仓库也没有,就去maven的中央仓库找。
一旦找到之后,就会把该jar包以及该jar包的所有依赖jar包通通下载到本地仓库。
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
使用maven工作时,
使用的是生命周期方法,
这些生命周期方法,是抽象的,具体干活的小弟,是插件。
插件只需要添加进pom文件即可, 不需要关注底层的工作。
maven工程,输出目录:target
最常用的生命周期方法:
clean: 清除工作目录。
package:打包 生成jar包
clean + package 一起使用
install: 把自己的项目达成jar包,然后发布到本地仓库中。
如果jar包最终的目录中,存在以.lastUpdate 为后缀的文件,记得要删除掉,然后重新下载。
1.5.案例1:Actor入门案例
一个Actor就是一个类,要想实现actor功能,需要实现Actor特质
需要重写receive方法,进行消息匹配接收
程序运行需要执行入口,所以需要有main方法。
actor必须通过ActorSystem来创建
// 通过字符串添加配置信息
val str =
"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "127.0.0.1"
|akka.remote.netty.tcp.port = "8888"
""".stripMargin
// 创建配置工厂
val conf = ConfigFactory.parseString(str)
// 创建actorsystem
val as: ActorSystem = ActorSystem.create("actorsystem-test",conf)
// 通过类型反射创建actor的引用对象
val actor: ActorRef = as.actorOf(Props[HelloActor],"actor-test")
// 向actor引用对象发送异步消息
actor ! "hello"
1.6.案例 人机Ping Pang大战
思路:
需要两个Actor,通过发送消息进行通信。
一个Actor在启动时,需要向另一个actor发送连接请求。
连接请求在preStart()方法中实现。
class AlphGo extends Actor{
// 表示接受的方法,进行模式匹配 会被调用多次
override def receive: Receive = {
}
}
object AlphGo{
def main(args: Array[String]): Unit = {
}
}
class MaLong extends Actor{
// 在构造器之后,receive方法之前执行
override def preStart(): Unit = {
// worker向master建立连接,并向master发送消息
// 四个关键参数: actorSystem的名称 actor的名称 ip地址,端口号
val proxy: ActorSelection = context.actorSelection("akka.tcp://1.并发编程模型Akka
1.1. Akka 介绍
多线程开发要处理并发,锁,线程