Scala多线程实现

2 篇文章 0 订阅
2 篇文章 0 订阅

Actor

  1. 什么是Scala Actor

(1)Scala中的Actor能够实现并行编程的强大功能,他是基于事件模板的并发机制。Scala是运用消息的发送接、收来实现多线程的。使用Scala能够更容易地实现多线程应用的开发。

 

多线程不是提高程序运行效率而是提高资源利用率

 

(2)并发(concurrency)和并行(parallellism)是:

解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。也就是说,并发可以是虚拟的同时执行,也可以是真的同时执行。

 

  1. 传统的java并发编程与Scala Actor编程的区别  

 

对于Java,我们都知道它的多线程实现需要对共享资源(变量、对象等)使用synchronized 关键字进行代码块同步、对象锁互斥等等。而且,常常一大块的try…catch语句块中加上wait方法、notify方法、notifyAll方法是让人很头疼的。原因就在于Java中多数使用的是可变状态的对象资源,对这些资源进行共享来实现多线程编程的话,控制好资源竞争与防止对象状态被意外修改是非常重要的,而对象状态的不变性也是较难以保证的。 而在Scala中,我们可以通过复制不可变状态的资源(即对象,Scala中一切都是对象,连函数、方法也是)的一个副本,再基于Actor的消息发送、接收机制进行并行编程

 

Scala中的并发编程:

①Scala中的并发编程思想与Java中的并发编程思想完全不一样,Scala中的Actor是一种不共享数据,依赖于消息传递的一种并发编程模式, 避免了死锁、资源争夺等情况。在具体实现的过程中,Scala中的Actor会不断的循环自己的邮箱,并通过receive偏函数进行消息的模式匹配并进行相应的处理。

②如果Actor A和 Actor B要相互沟通的话,首先A要给B传递一个消息,B会有一个收件箱,然后B会不断的循环自己的收件箱, 若看见A发过来的消息,B就会解析A的消息并执行,处理完之后就有可能将处理的结果通过邮件的方式发送给A。

  1. Actor方法执行顺序

(1)首先调用start()方法启动Actor 
(2)调用start()方法后其act()方法会被执行 
Scala Actor向Actor发送消息 

  1. 发送消息的方式

! 发送异步消息,没有返回值。 
!? 发送同步消息,等待返回值。 
!! 发送异步消息,返回值是 Future[Any]。 

  1.  
  2. 同步交互与异步交互

Java中交互方式分为同步消息处理和异步消息处理两种:
同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。
我们的项目开发中都会优先选择不需要等待的异步交互方式。同步交互比如银行的转账系统,对数据库的保存操作等等,都会使用同步交互操作,其余情况都优先使用异步交互。 

 

  1. Actor的案例应用

(1)创建Actor

//继承Actor特质类

class HiActor extends Actor {

  //重写Actor特质类的抽象方法act,从而实现自己的业务逻辑

  override def act(): Unit = {

    //消息循环

    while (true)

    //使用case模式匹配接收到的消息

      receive {

        case "Hi" => println("Hello")

      }

  }

}

  1. 启动Actor

 val actor1 = new HiActor

actor1.start()

 

 

  1. Actor执行是异步的(不需要等待返回结果,可以直接进行其他的线程)

 package actor
import scala.actors.Actor
//继承Actor特质类
class HiActor extends Actor {
  //重写Actor特质类的抽象方法act,从而实现自己的业务逻辑
  override def act(): Unit = {
    //消息循环
    while (true)
    //使用case模式匹配接收到的消息
      receive {
        case "Hi" =>{
          for(i<-1 to 10){
            println("this is Actor.....")
            Thread.sleep(3000);
            //sender ! "start Main....."
          }
        }
      }
  }
}
object Actor1 {
  def main(args: Array[String]): Unit = {
    val actor1=new HiActor()
    actor1.start()
    actor1 ! "Hi"
    for(i<-1 to 10){
      println("this is Main .....")
      Thread.sleep(3000)
    }
  }
}

 

结果:

this is Main .....

this is Actor.....

this is Main .....

this is Actor.....

this is Main .....

 

 

  1. 同步执行,没有返回结果(不会执行下一个请求)

  package actor
import scala.actors.Actor
//继承Actor特质类
class HiActor extends Actor {
  //重写Actor特质类的抽象方法act,从而实现自己的业务逻辑
  override def act(): Unit = {
    //消息循环
    while (true)
    //使用case模式匹配接收到的消息
      receive {
        case "Hi" =>{
          for(i<-1 to 10){
            println("this is Actor.....")
            Thread.sleep(3000);
            //sender ! "start Main....."
          }
        }
      }
  }
}
object Actor1 {
  def main(args: Array[String]): Unit = {
    val actor1=new HiActor()
    actor1.start()
    actor1 !? "Hi"
    for(i<-1 to 10){
      println("this is Main .....")
      Thread.sleep(3000)
    }
  }
}

 

结果:

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

this is Actor.....

 

 

  1.  同步执行有返回结果(会执行下一个请求)

 package actor

import scala.actors.Actor

//继承Actor特质类
class HiActor extends Actor {
  //重写Actor特质类的抽象方法act,从而实现自己的业务逻辑
  override def act(): Unit = {
    //消息循环
    while (true)
    //使用case模式匹配接收到的消息
      receive {
        case "Hi" =>{
          for(i<-1 to 10){
            println("this is Actor.....")
            Thread.sleep(3000);
            sender ! "start Main....."
          }
        }
      }
  }
}

object Actor1 {
  def main(args: Array[String]): Unit = {
    val actor1=new HiActor()
    actor1.start()
val replay=actor1 !? "Hi"
    println(replay)
    for(i<-1 to 10){
      println("this is Main .....")
      Thread.sleep(3000)
    }
  }
}

结果:

this is Actor.....

this is Actor.....

start Main.....

this is Main .....

this is Actor.....

this is Main .....

this is Actor.....

this is Main .....

this is Actor.....

this is Main .....

this is Actor.....

 

  1. 发送消息

actor1 ! "Hi"

 

  1. 用样例类作为消息,actor就可以使用样式匹配来处理消息了

 package actor
import scala.actors.Actor
//样例类
case class Person(val name:String,val age:Int)
//继承Actor特质类
class ActorTest extends Actor{
  override def act(): Unit = {
    while(true){
      receive{
        case Person(name,age)=>println(s"the name is ${name},the age is ${age}")
      }
    }
  }
}

object Actor2 {
  def main(args: Array[String]): Unit = {
    val act=new ActorTest()
    act.start()
    act ! Person("张三",18)
    act ! Person("李四",19)
    act ! Person("赵六",16)
  }
}

 

结果:

the name is 张三,the age is 18

the name is 李四,the age is 19

the name is 赵六,the age is 16

 

 

  1. 发送到actor的消息被存放在一个“邮箱”中。Receive方法从邮箱获取下一条消息并将它传递给它的参数,该参数是一个偏函数。 

receive{

case Deposit(amount) => ...

case Withdraw(amount) => ...

}

 

  1. 向其他的Actor发送消息

当运算被分拆到不同actor来并行处理问题的各个部分时,这些处理结果需要被收集到一起。Actor可以将结果存入到一个线程安全的数据结构当中,比如一个并发的哈希映射,但actor模型并不鼓励使用共享数据。因而当actor计算出结果后,应该向另一个actor发送消息。

Actor计算结果发送方向:

可以是一些全局的actor。不过,当actor数量很多时,该方案伸缩性并不好。

Actor可以构造成带有指向另一个或多个actor的引用。

Actor可以接收带有指向另一个actor的引用消息。

Actor可以返回消息给发送方。

  1.  共享线程

某些程序包含的actor过多,以至于要为actor创建单独的线程开销会很大。此时需要考虑在同一个线程中运行多个actor。Actor有时大部分时间用户等待消息,这时actor所在的单独线程会堵塞,与其这样不如用一个线程来执行多个actor的消息处理函数。

在Scala中,react方法可以实现这样的功能。react方法接收一个偏函数,并将它添加到邮箱,然后退出。

react工作原理

当你调用一个actor的start时,start方法会以某种方式来确保最终会有某个线程来调用那个actor的act方法。如果act方法调用了react ,则react方法会在actor的邮箱中查找传递给偏函数的能够处理的消息 。(和receive方法一样,传递待处理消息给偏函数的isDefinedAt方法。) 如果找到一个可以处理的消息,react 会安排一个在未来某个时间处理该消息的计划并抛出异常。如果它没有找到这样的消息,它会将actor置于“冷存储” 状态 ,在它通过邮箱收到更多消息时重新激活,并抛出异常。不论是哪种情况,react都会以这个异常的方式完成其执行,act 方法也随之结束  调用act的线程会捕获这个异常,忘掉这个actor , 并继续处理其他事务。这就是为什么你想要react在处理第一个消息之外做更多的事,你将需要在偏函数中再次调用act方法 ,或使用某种其他的手段来让react再次被调用。

不同actor可以通过react而不是receive来共享线程,前提是消息处理器的控制流转足够简单。

 

  import scala.actors.Actor
/**
  * scala Actor构建在java的线程基础之上的,
  * 为了避免频繁的线程创建、销毁和切换等,scala中提供了react方法
  * 方法执行完毕后,仍然被保留
  */
//样例类
case class Persion(val name:String,val age:Int)

case class Animal(val name:String,val age:Int)

class MyReact extends Actor{
  override def act(): Unit = {
    loop{

      react{
        case Person(name,age)=>{
          println(s"这是个人the name is ${name},the age is ${age}")
          Thread.sleep(2000)
          println(s"${name}会敲代码")
        }
        case Animal(name,age)=>{
          println(s"这是动物the name is ${name},the age is ${age}")
          Thread.sleep(2000)
          println(s"${name}会摇尾巴")
        }
        case _ =>println("不清楚是啥")
      }

    }
  }
}
object reactTest{
  def main(args: Array[String]): Unit = {
    val reac=new MyReact()
    reac.start()
    reac ! Person("蔡文姬",18)
    reac ! Animal("梦奇",16)
    println("完毕!")
  }
}

 

 

  1.  多线程wordCount

 package actor

import scala.actors.{Actor, Future}
import scala.collection.mutable.ListBuffer
import scala.io.Source

case class SbTask(fn:String)
case object StopTask

class WordCountByActorTask extends Actor {
  override def act(): Unit = {
    while(true) {
      receive {
        case SbTask(f) => {
          //通过Source.fromFile(f)读取文件,获取每行并且把它转换成List
          //List(aaa aaa,bbb bbb,ccc ccc )
          val lines : List[String] = Source.fromFile(f).getLines().toList;

          //将lines list里面的内容合并,然后并且将它split.
          //List(aaa,aaa,bbb,bbb,ccc,ccc)
          val words : List[String] = lines.flatMap(_.split(" "))
          //将word这些单词变成map的并且是元组类型的,每个都是1,----,记着对他们进行过分组,接着就是计算单词
          //Map(ddd -> 2, ggg -> 2, eee -> 2, ccc -> 2)
          val result: Map[String, Int]= words.map((_, 1)).groupBy(_._1).mapValues(_.size)
          sender ! result
        }
        case StopTask => {
          exit()
        }
      }
    }
  }
}
object WordCountByActor {
  def main(args: Array[String]): Unit = {

    val replys = new ListBuffer[Future[Any]] //Feture相当于给出了一种期望,就是说可能立即不会有结果,但是执行完毕或过一段时间后会有结果

    val results = new ListBuffer[Map[String,Int]]

    val files = Array("E://aaa//aaa.txt","E://aaa//bbb.txt","E://aaa//ccc.txt")
    //每个文件就启动多少个actor,并将每个actor处理后得到的结果存储到List中
    for(f <- files) {
      val t = new WordCountByActorTask
      t.start()
      //通过SbTask的方式发送文件名
      val reply:Future[Any] = t !! SbTask(f)
      //将处理到的结果放到ListBuffer中。
      //ListBuffer(Map(ddd -> 2, ggg -> 2, eee -> 2, ccc -> 2),Map(ddd -> 2, ggg -> 2, eee -> 2, ccc -> 2)...)
      replys += reply
    }
    //对获得的replay的值进行计算。
    while (replys.size > 0) {
      val dones: ListBuffer[Future[Any]] = replys.filter(_.isSet)
      for(f <- dones) {
        //从relay中拿值,并将它转成map
        results += f.apply().asInstanceOf[Map[String, Int]]
        //计算完成之后,将这个replay移除掉。
        replys -= f
      }
      Thread.sleep(500)
    }
    //ListBuffer(Map(ddd -> 2, ggg -> 2, eee -> 2, ccc -> 2),Map(ddd -> 2, ggg -> 2, eee -> 2, ccc -> 2)...)
    //.flatten
    //ListBuffer((ddd,2), (ggg,2), (eee,2), (ccc,2)  (ddd,2), (ggg,2), (eee,2), (ccc,2))
    //.groupBy
    //Map(ddd->ListBuffer((ddd,2),(ddd,2)),ggg->ListBuffer((ggg,2),(ggg,2)).......)
    //mapValues(_.foldLeft(0)(_+_._2))
    //ddd -> 6, ggg -> 6, eee -> 6, ccc -> 6

    println(results.flatten.groupBy(_._1).mapValues(_.foldLeft(0)(_+_._2)))
  }
}

 

 

  1. Actor的生命周期

actor的act方法在actor的start方法被调用的时候开始执行。通常,actor接下来做的事情是进入某个循环,例如:

 def act(): Unit ={
  while (...){
  receive{
  //
}
}

}

 

actor在如下情形之一会终止执行:

Act方法返回

Act方法由于异常被终止

Actor调用exit方法

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值