Scala教程(十八)并发编程详解
1 Scala并发Actor
1.1 创建和启动Actor
编写Actor并发程序,实现(trait)Actor特征,重写act方法,act方法与Java中的Runnable接口的run方法很相似。
正如不同的线程Actor的act方法也是可以并行运行的。
import scala.actors.Actor
/*
* Actor是一种基于事件的轻量级线程,使用Actor则需要关注操作数据的代码结构,因为减少了数据的共享。
* Actor的主要能力来源于消息传递,而不是采用阻塞调用的处理形式。
* 如果创建直接或间接扩展 Actor的类,要确保对对象的所有调用都通过消息传递进行。
*
* Actor提供了并发程序与传统的基于锁(比如:Java)结构不同。Actor使我们更容易设计正确、没死锁或争用状况的程序。
* 编写Actor并发程序,实现(trait)Actor特征,重写act方法,act方法与Java中的Runnable接口的run方法很相似。
* 正如不同的线程Actor的act方法也是可以并行运行的。
*/
object FirstActor extends Actor {
def act(): Unit = {
// 输出当前线程名称
println(Thread.currentThread().getName);
List.range(1, 11).foreach {
x =>
println("FirstActor==>" + x); // 输出执结次数
Thread.sleep(1000); // 程序1秒后再执行
}
}
}
object SecondActor extends Actor {
def act(): Unit = {
// 输出当前线程名称
println(Thread.currentThread().getName);
List.range(1, 11).foreach {
x =>
println("SecondActor:==>" + x); // 输出执结次数
Thread.sleep(1000); // 程序1秒后再执行
}
}
}
object HelloActor {
def main(args: Array[String]): Unit = {
// start方式启动多线程
FirstActor.start();
SecondActor.start();
}
}
1.2 发送/接收消息
Actor是一个处理异步消息对象。你向某个Actor发送消息,该Actor处理消息,或许还会向其它Actor发送消息做进一步处理。
消息可以是任何对象,发送消息的语法:actor ! message
import scala.actors.Actor
import scala.actors.Actor.actor
import scala.actors.Actor.receive
// 定义ActorMessage对象,继承Actor
object Actor_Message extends Actor {
// 重写act方法
def act(): Unit = {
/*
* 循环遍历receive - "邮箱" 将msg消息直接打印输出。
* receive偏函数,调用apply方法,调用isDefinedAt判断模式匹配中是否有定义case匹配,如果有case匹配,则返回true
* 没有对应的case匹配不会报错,会被忽略掉。receive所在的Actor会一直处理阻塞(一直等待处理)状态。
*/
while (true) {
receive {
case msg => println("Message content Actor from inbox:" + msg);
}
}
}
}
// 匿名actor
object ActorMessage {
def main(args: Array[String]): Unit = {
// 匿名actor立即执行
val actorMessage = actor {
while (true) {
// 偏函数代码块
receive {
// 模式匹配
case msg: Double => println("Double Number from inbox:" + msg);
case _ => println("Something Unkown");
}
}
}
// 命令Actor,函数调用
Actor_Message.start();
/*
* 发送异步消息,语法:actor ! message
* 执行结果:Message content Actor from inbox:Hadoop
*/
Actor_Message ! "Hadoop";
/*
* 发送PI
* 执行结果:Double Number from inbox:3.141592653589793
*/
actorMessage ! Math.PI;
// 执行结果:Something Unkown
actorMessage ! "Spark"
}
}
1.3 消息模式匹配
偏函数中接收case class对象类型的信息。通过模式匹配直接提取内容的信息。
import scala.actors.Actor
import scala.actors.Actor.self
// 定义case Person类
case class Person(name: String, age: Int);
class HelloActor extends Actor {
def act(): Unit = {
while (true) {
// 偏函数代码块
// 每个case都是线行的,不是并行的,不会存资源竞争的情况
receive {
// 模式匹配,可以从case class提取内容,提取方法从apply中
case Person(name, age) =>
// 执行结果:Name==>Spark:Age==>6
println("Name==>" + name + ":Age==>" + age);
sender ! "Echo!!!"; // 向主线程发送消息
// 其它匹配项,需要编写为无关的“邮件”占满“邮箱”进行其它处理。
case _ => println("Something else...")
}
}
}
}
object ActorWithCaseClass {
def main(args: Array[String]): Unit = {
val hiActor = new HelloActor();
// 启动多线程
hiActor.start();
// 发送消息
hiActor ! Person("Spark", 6);
// 接收返回的消息,执行结果:msg==>Echo!!!
self.receive { case msg => println("msg==>" + msg) };
}
}
1.4 react关键字
react:不返回具体的内容,其实返回的值是Nothing,该类型表示非正常退出。
每次对react的调用都会返回Nothing,从而清栈。下一个actor,重用当前actor所在的线程。
import scala.actors.Actor;
import scala.actors.Actor._;
import java.net.InetAddress;
import java.net.UnknownHostException;
case class Net(name: String, actor: Actor);
object NameResolver extends Actor {
def act(): Unit = {
/*
* react:不返回具体的内容,其实返回的值是Nothing,该类型表示非正常退出。
* 下次再调用自己本身act()方法,形成无限递归循环,这个递归不会占用很大的栈空间。
* 每次对react的调用都会返回Nothing,从而清栈。
* 下一个actor,重用当前actor所在的线程。
*/
react {
// case模式匹配
case Net(name, actor) =>
sender ! getIp(name);
act(); // 自已调用本身实现在递归循环
case "EXIT" =>
println("Name resolver exiting.");
act(); // 自已调用本身实现在递归循环
case msg =>
println("Unhandled message:" + msg);
act(); // 自已调用本身实现在递归循环
}
}
// 通过域名获取Ip地址
def getIp(name: String): Option[InetAddress] = {
try {
// 输出Ip地址
println(InetAddress.getByName(name));
// 解析的结果返回Some
return Some(InetAddress.getByName(name));
} catch {
// 抛出异常,返回None
case _: UnknownHostException => None;
}
}
}
object ActorMoreEffective {
def main(args: Array[String]): Unit = {
// 启动线程
NameResolver.start();
// 发送消息,自引用传递self
NameResolver ! Net("www.baidu.com", self)
// 接收返回的消息,1秒后超时,执行结果:Some(www.baidu.com/61.135.169.121)
println(self.receiveWithin(1000) { case x => x });
}
}
1.5 loop关键字
loop关键字可以制作一个无穷循环。
import scala.actors.Actor;
import scala.actors.Actor._;
import java.net.InetAddress;
import java.net.UnknownHostException;
case class Net(name: String, actor: Actor);
object NameResolver extends Actor {
def act(): Unit = {
/*
* 无限循环
* loopWhile可以添加循环条件,如:loopWhile(count < max)
*/
loop {
react {
// case模式匹配
case Net(name, actor) =>
sender ! getIp(name);
case msg =>
println("Unhandled message:" + msg);
}
}
}
// 通过域名获取Ip地址
def getIp(name: String): Option[InetAddress] = {
try {
// 输出Ip地址
println(InetAddress.getByName(name));
// 解析的结果返回Some
return Some(InetAddress.getByName(name));
} catch {
// 抛出异常,返回None
case _: UnknownHostException => None;
}
}
}
object ActorMoreEffective {
def main(args: Array[String]): Unit = {
// 启动线程
NameResolver.start();
// 发送消息,自引用传递self
NameResolver ! Net("www.baidu.com", self)
// 接收返回的消息,1秒后超时,执行结果:Some(www.baidu.com/61.135.169.121)
println(self.receiveWithin(1000) { case x => x });
}
}
——厚积薄发(yuanxw)