scala 2.10.x版本及之前使用的是内置的Actor。scala 2.11.x版本后将Akka作为并发编程实现。Akka也实现了类似scala Actor的模型,核心概念也是Actor。
概述
scala的Actor类似于Java中的多线程编程,但却有所不同。scala中的Actor尽可能地避免锁和共享状态,避免多线程并发时出现资源争用的情况,进而提升多线程编程的性能。
Java内置线程模型 | Scala Actor模型 |
---|---|
“共享数据-锁”模型 | 不共享数据 |
每个object有一个monitor,监视多线程对可变状态的对象资源的访问 | actor之间通过message通信,复制不可变状态的对象资源的一个副本,基于Actor消息发送、接收机制进行并发编程 |
加锁的代码用synchronized标识 | |
死锁问题 | |
每个线程内部顺序执行 | 每个actor内部顺序执行 |
实现步骤
Scala提供了Actor trait来让我们更方便地进行actor多线程编程,就Actor trait就类似于Java中的Thread和Runnable一样,是基础的多线程基类和接口。
首先调用start()方法启动Actor
调用start()方法后act()方法会被执行
actor内部使用receive/react和模式匹配接收消息
actor接收消息
方法 说明 ! 异步消息,发送方没有返回值 !? 同步消息,发送方等待返回值 !! 异步消息,发送方返回值是Future[Any]
方法操作
actor启动,调用act()方法
import scala.actors.Actor
object ActorTest {
def main(args: Array[String]): Unit = {
println("线程 启动!")
MyActor1.start()
MyActor2.start()
}
}
object MyActor1 extends Actor {
override def act(): Unit = {
for (i <- 11 to 19) {
println("actor01---" + i)
Thread.sleep(500)
}
}
}
object MyActor2 extends Actor {
override def act(): Unit = {
for (i <- 21 to 29) {
println("actor02---" + i)
Thread.sleep(500)
}
}
}
可以不间断地接收消息
object ActorTest {
def main(args: Array[String]): Unit = {
println("线程 启动!")
val actor1 = new MyActor1
actor1.start()
// 以下为异步消息,不等待返回
actor1 ! "start"
actor1 ! "stop"
println("消息发送完毕!")
}
}
class MyActor1 extends Actor {
override def act(): Unit = {
while (true) { //以下就是偏函数
receive {
case "start" => {
println("启动中...")
Thread.sleep(500)
println("启动完成!")
}
case "stop" => {
println("停止中...")
Thread.sleep(500)
println("停止完成!")
}
}
}
}
}
react方式会复用线程,比receive更高效
import scala.actors.Actor
object ActorTest {
def main(args: Array[String]): Unit = {
println("线程 启动!")
val actor1 = new MyActor1
actor1.start()
// 以下为异步消息,不等待返回
actor1 ! "start"
actor1 ! "stop"
println("消息发送完毕!")
}
}
class MyActor1 extends Actor {
override def act(): Unit = {
loop {
react {
case "start" => {
println("starting ...")
Thread.sleep(1000)
println("started")
}
case "stop" => {
println("stopping ...")
Thread.sleep(1000)
println("stopped ...")
}
}
}
}
}
结合case class发送消息
import scala.actors.Actor
class AppleActor extends Actor {
def act(): Unit = {
while (true) {
receive {
case "start" => println("starting ...")
case SyncMsg(id, msg) => {
println(id + ",sync " + msg)
Thread.sleep(5000)
sender ! ReplyMsg(3,"finished")
}
case AsyncMsg(id, msg) => {
println(id + ",async " + msg)
Thread.sleep(5000)
}
}
}
}
}
object AppleActor {
def main(args: Array[String]) {
val a = new AppleActor
a.start()
//异步消息
a ! AsyncMsg(1, "hello actor")
println("异步消息发送完成")
//同步消息
//val content = a.!?(1000, SyncMsg(2, "hello actor"))
//println(content)
val reply = a !! SyncMsg(2, "hello actor")
println(reply.isSet)
//println("123")
val c = reply.apply()
println(reply.isSet)
println(c)
}
}
case class SyncMsg(id : Int, msg: String)
case class AsyncMsg(id : Int, msg: String)
case class ReplyMsg(id : Int, msg: String)
WordCount案例
import java.io.File
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.io.Source
class Task extends Actor {
override def act(): Unit = {
loop {
react {
case SubmitTask(fileName) => {
val contents = Source.fromFile(new File(fileName)).mkString
val arr = contents.split("\r\n")
val result = arr.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.length)
//val result = arr.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.foldLeft(0)(_ + _._2))
sender ! ResultTask(result)
}
case StopTask => {
exit()
}
}
}
}
}
object WorkCount {
def main(args: Array[String]) {
val files = Array("c://words.txt", "c://words.log")
val replaySet = new mutable.HashSet[Future[Any]]
val resultList = new mutable.ListBuffer[ResultTask]
for(f <- files) {
val t = new Task
val replay = t.start() !! SubmitTask(f)
replaySet += replay
}
while(replaySet.size > 0){
val toCumpute = replaySet.filter(_.isSet)
for(r <- toCumpute){
val result = r.apply()
resultList += result.asInstanceOf[ResultTask]
replaySet.remove(r)
}
Thread.sleep(100)
}
val finalResult = resultList.map(_.result).flatten.groupBy(_._1).mapValues(x => x.foldLeft(0)(_ + _._2))
println(finalResult)
}
}
case class SubmitTask(fileName: String)
case object StopTask
case class ResultTask(result: Map[String, Int])
忠于技术,热爱分享。欢迎关注公众号:java大数据编程,了解更多技术内容。