【大纲】
【正文】
1.【结论】特点
0.scala的Actor并发编程模型,是基于事件模型的并发机制,不共享数据,依赖消息传递。可有效避免资源争夺、死锁,比java多线程效率更高
1.scala在2.11.x版本由Akka并发编程框架代替了Actor
2.使用示例--并发词频统计:统计3个文件中的单词的词频汇总,则创建3个Actor,一人一个文件,并发去分别统计3个文件的词频,等都统计完成后,回收结果给主Actor,进行汇总计算
2.Actor多线程
2.1.【结论】代码步骤
0.定义一个class或object作为一个Actor类,继承Actor特质,创建一个Actor对象只单独使用一个线程,但整个Actor对象的生命周期有可能因为空闲了线程被销毁创建有事情做了又重新创建线程累计使用过多个线程号,线程能否复用要看具体的代码实现 # 注意:用class,可创建多个Actor实例;用object,则是单例的Actor对象 # 注意:复用线程后边会讲 在 优化持续接收消息 处
1.重写方法act(),方法体写你想让这个线程干的事情
2.启动Actor让这个线程干活:Actor对象.start() # 注意:start()会自动调用act()干活,干完活会自动调用exit()结束线程
3.如果需要多个线程,就定义多个Actor实例
4.每个Actor之间并行执行,互不干扰
# 注意:main()本身就等价于一个Actor的act()
2.2.【结论】代码示例
import scala.actors.Actor
object Actor1 {
object Actor1 extends Actor{
override def act(): Unit = for(i <- 1 to 10)println("actor1:" + i)
}
object Actor2 extends Actor{
override def act(): Unit = for(i <- 11 to 20)println("actor2:" + i)
}
def main(args: Array[String]): Unit = {
Actor1.start()
Actor2.start()
}
}
3.Actor收发消息
3.1.【结论】理论基础–发送消息符号
!? 发送同步消息,等待返回值
! 发送异步消息,没有返回值
!! 发送异步消息,返回值是Future[Any]
3.2.【结论】理论基础–Actor发送消息语法
Actor1给Actor2发送异步的字符串消息,无返回值:
object Actor1 extends Actor {
override def act(): Unit = {
//发送一句话给Actor2
Actor2 ! "你好啊, 我是Actor1!"
//发送第二句话
Actor2 ! "你叫什么名字呀? "
}
}
3.3.【结论】理论基础–Actor接收消息语法
Actor2通过recevice(偏函数)来接收其他Actor传来的消息
object Actor2 extends Actor {
override def act(): Unit = {
// 接收发送过来的消息
receive ({
case x: String => println(x)
})
}
}
3.4.【结论】理论基础–发送及接收一个消息
# 需求:
ActorA给ActorB异步发送一个字符串,ActorB接收到该消息后打印出来
# 实现
object Actor1 {
object ActorA extends Actor{
override def act(): Unit = {
ActorB ! "你好,我是ActorA"
ActorB ! "你在家不"
}
}
object ActorB extends Actor{
override def act(): Unit = {
receive({ // 只接收一次消息
case x:String => println(x)
})
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果:
你好,我是ActorA # 注意:ActorB只接收了一个消息,没有接收第二个消息“你在家不”
3.5.理论基础–while持续发送和while持续接收
3.5.1.【结论】特点
如果用while(true)循环来持续接收消息,在当前ActorB没有接收到消息时,线程会进入阻塞状态,又来新消息时,会重新创建线程来处理;如果有很多Actor,则可能会有很多线程都进入了阻塞状态,然后发生频繁的线程创建、销毁、切换,浪费资源
# 需求
ActorA一直给ActorB发送异步消息,ActorB一直接收消息
# 实现
import scala.actors.Actor
import scala.actors.threadpool.TimeUnit
object Actor1 {
object ActorA extends Actor{
override def act(): Unit = {
while(true){
ActorB ! "hahaha"
TimeUnit.SECONDS.sleep(3) // 休眠3秒
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
while(true){
receive({
case x:String => println(x)
})
}
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果:
无限循环打印hahaha
3.6.理论基础–while持续发送和loop持续接收
3.6.1.【结论】特点
ActorB用loop({react({偏函数})})方式来循环接收消息,可复用线程,避免ActorB的线程切换来浪费资源
# 需求
ActorA一直给ActorB发送异步消息,ActorB一直接收消息
# 实现
import scala.actors.Actor
import scala.actors.threadpool.TimeUnit
object Actor1 {
object ActorA extends Actor{
override def act(): Unit = {
while(true){
ActorB ! "hahaha"
TimeUnit.SECONDS.sleep(3) // 休眠3秒
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
loop({
receive({
case x:String => println(x)
})
})
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
3.7.【结论】理论基础–发送接收自定义消息
# 需求
ActorA一直给ActorB发送异步消息,ActorB一直接收并打印消息,消息使用样例类封装的
# 实现
import scala.actors.Actor
import scala.actors.threadpool.TimeUnit
object Actor1 {
case class SendMessage(id:Int, message:String)
object ActorA extends Actor{
override def act(): Unit = {
while(true){
ActorB ! SendMessage(1,"haha")
TimeUnit.SECONDS.sleep(3)
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
loop({
react({
case SendMessage(id, message) => println("我是ActorB,我收到的消息是:"+ id + message)
})
})
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果:
无限循环打印 我是ActorB,我收到的消息是:1haha
3.8.【结论】理论基础–接收并回复消息
# 需求
ActorA一直给ActorB发送同步消息,ActorB一直接收消息,接收一条后打印并回复一条消息,消息是使用样例类封装的
# 实现
import scala.actors.Actor
import scala.actors.threadpool.TimeUnit
object Actor1 {
case class SendMessage(id:Int, message:String)
case class ReceiveMessage(message:String)
object ActorA extends Actor{
override def act(): Unit = {
while(true) {
val re:Any = ActorB !? SendMessage(1,"消息1")
val result = re.asInstanceOf[ReceiveMessage]
println(result)
TimeUnit.SECONDS.sleep(3)
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
loop({
react({
case SendMessage(id, message) => println(message)
sender ! ReceiveMessage("消息2") // sender代表发送者对象
})
})
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果:
循环打印 消息1 ReceiveMessage(消息2)
3.9.实战–发送接收消息
3.9.1.【结论】发送异步无返回消息
# 需求
ActorA一直给ActorB发送异步无返回消息,ActorB一直接收并打印消息,消息使用样例类封装的
# 实现
object Actor1 {
case class SendMessage(id:Int, message:String)
object ActorA extends Actor{
override def act(): Unit = {
while(true){
ActorB ! SendMessage(1, "消息1")
TimeUnit.SECONDS.sleep(3)
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
loop{
react({
case SendMessage(id, message) => println(message)
})
}
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果:
循环打印 消息1
3.9.2.【结论】发送同步有返回消息
# 需求
ActorA一直给ActorB发送同步有返回消息,ActorB一直接收消息,接收到一条就打印并回复一条消息,ActorA接收到回复的消息就打印出来,消息使用样例类封装的
# 实现
object Actor1 {
case class SendMessage(id:Int, message:String)
case class ReceiveMessage(message:String)
object ActorA extends Actor{
override def act(): Unit = {
while(true){
val re:Any = ActorB !? SendMessage(1, "消息1")
val result = re.asInstanceOf[ReceiveMessage]
println(result)
TimeUnit.SECONDS.sleep(3)
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
loop{
react({
case SendMessage(id, message) => println(message)
sender ! ReceiveMessage("消息2")
})
}
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果
循环打印 消息1 ReceiveMessage(消息2) # 注意:如果注释掉 sender这行,ActorA拿不到返回值,会一直阻塞住
3.9.3.【结论】发送异步有返回消息
# 需求
ActorA一直给ActorB发送异步有返回消息,ActorB一直接收消息,接收到一条就打印并回复一条消息,ActorA接收到回复的消息就打印出来,消息使用样例类封装的
# 实现
import scala.actors.Actor
import scala.actors.threadpool.TimeUnit
object Actor1 {
case class SendMessage(id:Int, message:String)
case class ReceiveMessage(message:String)
object ActorA extends Actor{
override def act(): Unit = {
while(true){
val future:Future[Any] = ActorB !! SendMessage(1, "消息1")
// 因为是异步的,future中不一定会立马有数据,一直循环等着
while(!future.isSet){println("我还没收到数据,在循环等等,什么时候有数据了再打破循环")}
val re = future.apply() // future.apply() 获取返回的数据
// future.isSet 为true代表有数据
val result = re.asInstanceOf[ReceiveMessage]
println(result)
TimeUnit.SECONDS.sleep(3)
}
}
}
object ActorB extends Actor{
override def act(): Unit = {
loop{
react({
case SendMessage(id, message) => println(message)
sender ! ReceiveMessage("消息2")
})
}
}
}
def main(args: Array[String]): Unit = {
ActorA.start()
ActorB.start()
}
}
# 效果:
循环打印下边这段
消息1
我还没收到数据,在循环等等,什么时候有数据了再打破循环
我还没收到数据,在循环等等,什么时候有数据了再打破循环
ReceiveMessage(消息2)
T.TODO
T.1.并发词频统计
# 需求
并发统计3个文件中的单词的词频汇总。则创建3个Actor,一人一个文件,并发去分别统计3个文件的词频,等都统计完成后,回收结果给主Actor,进行汇总计算
细节,用class定义Actor模板,然后可以new出3个Actor对象,并发使用。如果用object定义Actor模板,则创建多少对象实际上都是在用一个单例对象
./data/a.txt文件内容
hadoop sqoop hadoop
hadoop hadoop flume
hadoop hadoop hadoop
spark
./data/b.txt文件内容
flink hadoop hive
hadoop sqoop hadoop
hadoop hadoop hadoop
spark
./data/c.txt文件内容
spark sqoop hadoop
spark hadoop flume
spark hadoop hadoop
spark
# 实现
import java.io.File
import scala.actors.{Actor, Future}
import scala.io.Source
object Test6 {
case class FileName(fileName: String)
class WordCountActor extends Actor{
override def act(): Unit = {
loop({
react({
case FileName(fileName) =>
// println(fileName)
val lineList = Source.fromFile(fileName).getLines().toList
var allWordList = lineList.map(x => {x.split(" ").toList}).flatten
val wordCountList = allWordList.groupBy(x => x).map(x => x._1 -> x._2.length).toList
// println(wordCountList)
sender ! wordCountList
})
})
}
}
def main(args: Array[String]): Unit = {
var dir = "./data/"
val fileDir = new File(dir)
val fileNameList = fileDir.list().toList.map(x => dir + x)
// println(fileNameList)
val wordCountActorList = Traversable.fill(fileNameList.size)(new WordCountActor()).toList
val actorWithFile = wordCountActorList.zip(fileNameList)
// println(actorWithFile)
val futureList = actorWithFile.map { // map是并发执行
keyVal =>
val actor = keyVal._1
actor.start()
val future: Future[Any] = actor !! FileName(keyVal._2)
future
}
// println(futureList)
while(futureList.filter(x => !x.isSet).size != 0){println("没全收到,再等等")}
val m = futureList.map{_.apply().asInstanceOf[List[(String,Int)]]}.flatten.groupBy(x => x._1)
// println(m)
val result = m.map{
x =>
x._1 -> x._2.map({
y => y._2
}).sum
}
println(result)
}
}
# 效果:
没全收到,再等等
没全收到,再等等
Map(sqoop -> 3, flink -> 1, hadoop -> 17, spark -> 6, hive -> 1, flume -> 2)