Scala中的Actor入门笔记

时间:2022-09-08 21:29:05

核心内容:
1、Java中的并发编程思想与Scala中的并发编程思想
2、Scala中Actor的两种创建方式、Actor中的receive偏函数在进行模式匹配时与传统模式匹配的区别、receive具体的实现过程
3、Scala中的原生线程(即主线程)所在的Actor的获取方式、基于Actor的Case class的消息传递和Actor模型认知以及相应的优化方式
4、线程重用的概念以及线程重用的方式


1、Java中的并发编程思想与Scala中的并发编程思想

Java中的并发编程:
①Java中的并发编程基本上满足了事件之间相互独立,但是事件能够同时发生的场景的需要。
②Java中的并发编程是基于共享数据和加锁的一种机制,即会有一个共享的数据,然后有若干个线程去访问这个共享的数据(主要是对这个共享的数据进行修改),同时Java利用加锁的机制(即synchronized)来确保同一时间只有一个线程对我们的共享数据进行访问,进而保证共享数据的一致性。
③Java中的并发编程存在资源争夺和死锁等多种问题,因此程序越大问题越麻烦。
Scala中的并发编程:
①Scala中的并发编程思想与Java中的并发编程思想完全不一样,Scala中的Actor是一种不共享数据,依赖于消息传递的一种并发编程模式, 避免了死锁、资源争夺等情况。在具体实现的过程中,Scala中的Actor会不断的循环自己的邮箱,并通过receive偏函数进行消息的模式匹配并进行相应的处理。
②如果Actor A和 Actor B要相互沟通的话,首先A要给B传递一个消息,B会有一个收件箱,然后B会不断的循环自己的收件箱, 若看见A发过来的消息,B就会解析A的消息并执行,处理完之后就有可能将处理的结果通过邮件的方式发送给A。


实例程序1:

class A implements Runnable {
private int tickets = 1000; //共享数据
public void run() {
while(true){
synchronized(this){ //加锁的机制
if(tickets > 0){
System.out.println(Thread.currentThread().getName()+"正在售第:"+tickets+"张票!");
tickets --;
}
}
}
}
}
public class App
{

public static void main(String[] args) {
A aa = new A();
Thread t1 = new Thread(aa);
Thread t2 = new Thread(aa);
t1.setName("窗口1");
t2.setName("窗口2");
t1.start();
t2.start();
}
}

实例程序2:

class S extends Actor
{
override def act() =
{
for(i<- 1 to 100)
{
println("Futher is:"+i)
}
}
}
object App
{
def main(args: Array[String]): Unit =
{
val s1 = new S()
s1.start()
val aa = actor
{
for(i<- 1 to 100)
{
println("RichFuther is:"+i)
}
}
}
}
2、Scala中Actor的两种创建方式、Actor中的receive偏函数在进行模式匹配时与传统模式匹配的区别、receive具体的实现过程

①Scala中Actor的两种创建方式:
继承Actor,并实现act()方法;通过工具方法act来创建。匿名的Actor在创建的过程中将会自动启动,但是第一种方式必须要通过调用start()方法才会启动。虽然匿名的Actor与实名的Actor工作原理一样,但是Scala当中更倾向于使用匿名的Actor 。
②在scala的模式匹配当中,我们一般用不同的case匹配备选项,如果没有相应case的话,程序就会抛出异常; 但是在scala当中,偏函数receive在进行模式匹配的时候,如果没有相应case的话,程序不会抛出异常,而是悄悄的忽略掉这个消息。
③actor中receive偏函数具体的运行机制:偏函数receiv首先会通过isDefinedAt这个方法判断收件箱中的消息是否符合定义(打算处理),如果是的话, 它才会将这个消息交给我们偏函数中的apply方法进行模式匹配进行处理,如果这个消息不打算处理(false)的话,偏函数receive则会忽略掉这个消息。


Scala中的Actor入门笔记


3、Scala中的原生线程(即主线程)所在的Actor的获取方式、基于Actor的Case class的消息传递和Actor模型认知以及相应的优化方式

①Scala中的原生线程(即主线程)可以看做是一个Actor,当它需要接受并处理消息的时候,直接调用Actor伴生对象的self方法即可返回一个Actor实例对象,然后通过Actor实例对象的receive偏函数就可以进行消息的模式匹配并进行相应的处理。
②Scala中的case class与case object有两种功能:消息传递与模式匹配。在声明case class的过程中,若在扩展的属性前面没有加修饰符var与val,默认的修饰符为val,意味着在消息传递的过程中的属性是不可变的,因此特别适合消息传递。在Scala的Actor当中,case class与case object常与Actor进行结合使用。
③Actor的模型(大概图示:)
Scala中的Actor入门笔记
为了避免Actor接受到的消息无法匹配到receive偏函数中的case备选项,进而导致Actor的邮箱被一些无关的信息占满,一般情况下,我们会在receive偏函数中加一个case _备选项,使得receive方法可以处理掉邮箱中收到的所有消息,进而避免邮箱被无关的消息占满。
④不同Actor之间可以进行消息通信,Spark不同组件之间的通信大部分都是通过Akka的方式,而Akka是基于Scala中的Actor。


实例程序1:
Scala中的Actor入门笔记


4、线程重用的概念以及线程重用的方式

①所谓线程重用就是一个线程执行完毕之后,这个线程并没有立即被销毁、回收,而是可以继续被使用。
②在Java当中,所谓执行一个线程,就是执行本线程所对应的run方法,若run方法里面的代码执行完毕,这个线程就会结束,此时线程默认就会得到回收(销毁),因此为了使线程得到重用,即不被销毁、回收,我们常常将run方法里面的代码块加上一个循环。
③在Java当中,线程重用的具体方式是run方法里面的代码加上循环,在Scala当中,线程重用的方式就是Actor通过while(true)或者loop循环不断的关联自己的邮箱,使自己的邮箱不断的处于循环状态。


实例程序1:
Scala中的Actor入门笔记


如有问题,欢迎大家留言指正!