国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

從Java走進Scala:一步步教你使用Scala Actor

開發(fā) 后端
“actor” 實現(xiàn)在稱為 actor 的執(zhí)行實體之間使用消息傳遞進行協(xié)作,而Scala Actor是Scala并發(fā)編程中最重要的一個機制。本文生趣的介紹了如何使用Scala Actor。

前一篇文章 中,我討論了構(gòu)建并發(fā)代碼的重要性(無論是否是 Scala 代碼),還討論了在編寫并發(fā)代碼時開發(fā)人員面對的一些問題,包括不要鎖住太多東西、不要鎖住太少東西、避免死鎖、避免生成太多線程等等。

51CTO編輯推薦:Scala編程語言專題

這些理論問題太沉悶了。為了避免讀者覺得失望,我與您一起研究了 Scala 的一些并發(fā)構(gòu)造,首先是在 Scala 中直接使用 Java 語言的并發(fā)庫的基本方法,然后討論 Scala API 中的 MailBox 類型。盡管這兩種方法都是可行的,但是它們并不是 Scala 實現(xiàn)并發(fā)性的主要機制。

真正提供并發(fā)性的是 Scala 的 actor。

什么是 “actor”?

“actor” 實現(xiàn)在稱為 actor 的執(zhí)行實體之間使用消息傳遞進行協(xié)作(注意,這里有意避免使用 “進程”、“線程” 或 “機器” 等詞匯)。盡管它聽起來與 RPC 機制有點兒相似,但是它們是有區(qū)別的。RPC 調(diào)用(比如 Java RMI 調(diào)用)會在調(diào)用者端阻塞,直到服務(wù)器端完成處理并發(fā)送回某種響應(yīng)(返回值或異常),而消息傳遞方法不會阻塞調(diào)用者,因此可以巧妙地避免死鎖。

僅僅傳遞消息并不能避免錯誤的并發(fā)代碼的所有問題。另外,這種方法還有助于使用 “不共享任何東西” 編程風(fēng)格,也就是說不同的 actor 并不訪問共享的數(shù)據(jù)結(jié)構(gòu)(這有助于促進封裝 actor,無論 actor 是 JVM 本地的,還是位于其他地方) — 這樣就完全不需要同步了。畢竟,如果不共享任何東西,并發(fā)執(zhí)行就不涉及任何需要同步的東西。

這不算是對 actor 模型的正規(guī)描述,而且毫無疑問,具有更正規(guī)的計算機科學(xué)背景的人會找到各種更嚴(yán)謹(jǐn)?shù)拿枋龇椒ǎ軌蛎枋?actor 的所有細(xì)節(jié)。但是對于本文來說,這個描述已經(jīng)夠了。在網(wǎng)上可以找到更詳細(xì)更正規(guī)的描述,還有一些學(xué)術(shù)文章詳細(xì)討論了 actor 背后的概念(請您自己決定是否要深入學(xué)習(xí)這些概念)。現(xiàn)在,我們來看看 Scala actors API。

#p#

Scala actor

使用 actor 根本不困難,只需使用 Actor 類的 actor 方法創(chuàng)建一個 actor,見清單 1:

清單 1. 開拍!

  1. import scala.actors._, Actor._  
  2.  
  3. package com.tedneward.scalaexamples.scala.V4  
  4. {  
  5.   object Actor1  
  6.   {  
  7.     def main(args : Array[String]) =  
  8.     {  
  9.       val badActor =  
  10.         actor  
  11.         {  
  12.           receive  
  13.           {  
  14.             case msg => System.out.println(msg)  
  15.           }  
  16.         }  
  17.         
  18.       badActor ! "Do ya feel lucky, punk?" 
  19.     }  
  20.   }  
  21. }  
  22.    

這里同時做了兩件事。

首先,我們從 Scala Actors 庫的包中導(dǎo)入了這個庫,然后從庫中直接導(dǎo)入了 Actor 類的成員;第二步并不是完全必要的,因為在后面的代碼中可以使用 Actor.actor 替代 actor,但是這么做能夠表明 actor 是語言的內(nèi)置結(jié)構(gòu)并(在一定程度上)提高代碼的可讀性。

下一步是使用 actor 方法創(chuàng)建 actor 本身,這個方法通過參數(shù)接收一個代碼塊。在這里,代碼塊執(zhí)行一個簡單的 receive(稍后討論)。結(jié)果是一個 actor,它被存儲在一個值引用中,供以后使用。

請記住,除了消息之外,actor 不使用其他通信方法。使用 ! 的代碼行實際上是一個向 badActor 發(fā)送消息的方法,這可能不太直觀。Actor 內(nèi)部還包含另一個 MailBox 元素(已討論);! 方法接收傳遞過來的參數(shù)(在這里是一個字符串),把它發(fā)送給郵箱,然后立即返回。

消息交付給 actor 之后,actor 通過調(diào)用它的 receive 方法來處理消息;這個方法從郵箱中取出第一個可用的消息,把它交付給一個模式匹配塊。注意,因為這里沒有指定模式匹配的類型,所以任何消息都是匹配的,而且消息被綁定到 msg 名稱(為了打印它)。

一定要注意一點:對于可以發(fā)送的類型,沒有任何限制 — 不一定要像前面的示例那樣發(fā)送字符串。實際上,基于 actor 的設(shè)計常常使用 Scala case 類攜帶實際消息本身,這樣就可以根據(jù) case 類的參數(shù)/成員的類型提供隱式的 “命令” 或 “動作”,或者向動作提供數(shù)據(jù)。

例如,假設(shè)希望 actor 用兩個不同的動作來響應(yīng)發(fā)送的消息;新的實現(xiàn)可能與清單 2 相似:

清單 2. 嗨,我是導(dǎo)演!

  1. object Actor2  
  2. {  
  3.   case class Speak(line : String);  
  4.   case class Gesture(bodyPart : String, action : String);  
  5.   case class NegotiateNewContract;  
  6.  
  7.   def main(args : Array[String]) =  
  8.   {  
  9.     val badActor =  
  10.       actor  
  11.       {  
  12.         receive  
  13.         {  
  14.           case NegotiateNewContract =>  
  15.             System.out.println("I won't do it for less than $1 million!")  
  16.           case Speak(line) =>  
  17.             System.out.println(line)  
  18.           case Gesture(bodyPart, action) =>  
  19.             System.out.println("(" + action + "s " + bodyPart + ")")  
  20.           case _ =>  
  21.             System.out.println("Huh? I'll be in my trailer.")  
  22.         }  
  23.       }  
  24.       
  25.     badActor ! NegotiateNewContract  
  26.     badActor ! Speak("Do ya feel lucky, punk?")  
  27.     badActor ! Gesture("face""grimaces")  
  28.     badActor ! Speak("Well, do ya?")  
  29.   }  
  30. }  

到目前為止,看起來似乎沒問題,但是在運行時,只協(xié)商了新合同;在此之后,JVM 終止了。初看上去,似乎是生成的線程無法足夠快地響應(yīng)消息,但是要記住在 actor 模型中并不處理線程,只處理消息傳遞。這里的問題其實非常簡單:一次接收使用一個消息,所以無論隊列中有多少個消息正在等待處理都無所謂,因為只有一次接收,所以只交付一個消息。

糾正這個問題需要對代碼做以下修改,見清單 3:

◆把 receive 塊放在一個接近無限的循環(huán)中。

◆創(chuàng)建一個新的 case 類來表示什么時候處理全部完成了。

清單 3. 現(xiàn)在我是一個更好的導(dǎo)演!

  1. object Actor2  
  2. {  
  3.   case class Speak(line : String);  
  4.   case class Gesture(bodyPart : String, action : String);  
  5.   case class NegotiateNewContract;  
  6.   case class ThatsAWrap;  
  7.  
  8.   def main(args : Array[String]) =  
  9.   {  
  10.     val badActor =  
  11.       actor  
  12.       {  
  13.         var done = false 
  14.         while (! done)  
  15.         {  
  16.           receive  
  17.           {  
  18.             case NegotiateNewContract =>  
  19.               System.out.println("I won't do it for less than $1 million!")  
  20.             case Speak(line) =>  
  21.               System.out.println(line)  
  22.             case Gesture(bodyPart, action) =>  
  23.               System.out.println("(" + action + "s " + bodyPart + ")")  
  24.             case ThatsAWrap =>  
  25.               System.out.println("Great cast party, everybody! See ya!")  
  26.               done = true 
  27.             case _ =>  
  28.               System.out.println("Huh? I'll be in my trailer.")  
  29.           }  
  30.         }  
  31.       }  
  32.       
  33.     badActor ! NegotiateNewContract  
  34.     badActor ! Speak("Do ya feel lucky, punk?")  
  35.     badActor ! Gesture("face""grimaces")  
  36.     badActor ! Speak("Well, do ya?")  
  37.     badActor ! ThatsAWrap  
  38.   }  
  39. }  

這下行了!使用 Scala actor 就這么容易。

#p#

并發(fā)地執(zhí)行動作

上面的代碼沒有反映出并發(fā)性 — 到目前為止給出的代碼更像是另一種異步的方法調(diào)用形式,您看不出區(qū)別。(從技術(shù)上說,在第二個示例中引入接近無限循環(huán)之前的代碼中,可以猜出有一定的并發(fā)性存在,但這只是偶然的證據(jù),不是明確的證明)。

為了證明在幕后確實有多個線程存在,我們深入研究一下前一個示例:

清單 4. 我要拍特寫了

  1. object Actor3  
  2. {  
  3.   case class Speak(line : String);  
  4.   case class Gesture(bodyPart : String, action : String);  
  5.   case class NegotiateNewContract;  
  6.   case class ThatsAWrap;  
  7.  
  8.   def main(args : Array[String]) =  
  9.   {  
  10.     def ct =  
  11.       "Thread " + Thread.currentThread().getName() + ": " 
  12.     val badActor =  
  13.       actor  
  14.       {  
  15.         var done = false 
  16.         while (! done)  
  17.         {  
  18.           receive  
  19.           {  
  20.             case NegotiateNewContract =>  
  21.               System.out.println(ct + "I won't do it for less than $1 million!")  
  22.             case Speak(line) =>  
  23.               System.out.println(ct + line)  
  24.             case Gesture(bodyPart, action) =>  
  25.               System.out.println(ct + "(" + action + "s " + bodyPart + ")")  
  26.             case ThatsAWrap =>  
  27.               System.out.println(ct + "Great cast party, everybody! See ya!")  
  28.               done = true 
  29.             case _ =>  
  30.               System.out.println(ct + "Huh? I'll be in my trailer.")  
  31.           }  
  32.         }  
  33.       }  
  34.       
  35.     System.out.println(ct + "Negotiating...")  
  36.     badActor ! NegotiateNewContract  
  37.     System.out.println(ct + "Speaking...")  
  38.     badActor ! Speak("Do ya feel lucky, punk?")  
  39.     System.out.println(ct + "Gesturing...")  
  40.     badActor ! Gesture("face""grimaces")  
  41.     System.out.println(ct + "Speaking again...")  
  42.     badActor ! Speak("Well, do ya?")  
  43.     System.out.println(ct + "Wrapping up")  
  44.     badActor ! ThatsAWrap  
  45.   }  
  46. }  

運行這個新示例,就會非常明確地發(fā)現(xiàn)確實有兩個不同的線程:

◆main 線程(所有 Java 程序都以它開始)

◆Thread-2 線程,它是 Scala Actors 庫在幕后生成的

因此,在啟動第一個 actor 時,本質(zhì)上已經(jīng)開始了多線程執(zhí)行。

但是,習(xí)慣這種新的執(zhí)行模型可能有點兒困難,因為這是一種全新的并發(fā)性考慮方式。例如,請考慮 前一篇文章 中的 Producer/Consumer 模型。那里有大量代碼,尤其是在 Drop 類中,我們可以清楚地看到線程之間,以及線程與保證所有東西同步的監(jiān)視器之間有哪些交互活動。為了便于參考,我在這里給出前一篇文章中的 V3 代碼:

清單 5. ProdConSample,v3 (Scala)

  1. package com.tedneward.scalaexamples.scala.V3  
  2. {  
  3.   import concurrent.MailBox  
  4.   import concurrent.ops._  
  5.  
  6.   object ProdConSample  
  7.   {  
  8.     class Drop  
  9.     {  
  10.       private val m = new MailBox()  
  11.         
  12.       private case class Empty()  
  13.       private case class Full(x : String)  
  14.         
  15.       m send Empty()  // initialization  
  16.         
  17.       def put(msg : String) : Unit =  
  18.       {  
  19.         m receive  
  20.         {  
  21.           case Empty() =>  
  22.             m send Full(msg)  
  23.         }  
  24.       }  
  25.         
  26.       def take() : String =  
  27.       {  
  28.         m receive  
  29.         {  
  30.           case Full(msg) =>  
  31.             m send Empty(); msg  
  32.         }  
  33.       }  
  34.     }  
  35.     
  36.     def main(args : Array[String]) : Unit =  
  37.     {  
  38.       // Create Drop  
  39.       val drop = new Drop()  
  40.         
  41.       // Spawn Producer  
  42.       spawn  
  43.       {  
  44.         val importantInfo : Array[String] = Array(  
  45.           "Mares eat oats",  
  46.           "Does eat oats",  
  47.           "Little lambs eat ivy",  
  48.           "A kid will eat ivy too" 
  49.         );  
  50.           
  51.         importantInfo.foreach((msg) => drop.put(msg))  
  52.         drop.put("DONE")  
  53.       }  
  54.         
  55.       // Spawn Consumer  
  56.       spawn  
  57.       {  
  58.         var message = drop.take()  
  59.         while (message != "DONE")  
  60.         {  
  61.           System.out.format("MESSAGE RECEIVED: %s%n", message)  
  62.           message = drop.take()  
  63.         }  
  64.       }  
  65.     }  
  66.   }  
  67. }  
  68.    

盡管看到 Scala 如何簡化這些代碼很有意思,但是它實際上與原來的 Java 版本沒有概念性差異。現(xiàn)在,看看如果把 Producer/Consumer 示例的基于 actor 的版本縮減到最基本的形式,它會是什么樣子:

清單 6. Take 1,開拍!生產(chǎn)!消費!

  1. object ProdConSample1  
  2. {  
  3.   case class Message(msg : String)  
  4.     
  5.   def main(args : Array[String]) : Unit =  
  6.   {  
  7.     val consumer =  
  8.       actor  
  9.       {  
  10.         var done = false 
  11.         while (! done)  
  12.         {  
  13.           receive  
  14.           {  
  15.             case msg =>  
  16.               System.out.println("Received message! -> " + msg)  
  17.               done = (msg == "DONE")  
  18.           }  
  19.         }  
  20.       }  
  21.       
  22.     consumer ! "Mares eat oats" 
  23.     consumer ! "Does eat oats" 
  24.     consumer ! "Little lambs eat ivy" 
  25.     consumer ! "Kids eat ivy too" 
  26.     consumer ! "DONE"        
  27.   }  
  28. }  

第一個版本確實簡短多了,而且在某些情況下可能能夠完成所需的所有工作;但是,如果運行這段代碼并與以前的版本做比較,就會發(fā)現(xiàn)一個重要的差異 — 基于 actor 的版本是一個多位置緩沖區(qū),而不是我們以前使用的單位置緩沖。這看起來是一項改進,而不是缺陷,但是我們要通過對比確認(rèn)這一點。我們來創(chuàng)建 Drop 的基于 actor 的版本,在這個版本中所有對 put() 的調(diào)用必須由對 take() 的調(diào)用進行平衡。

幸運的是,Scala Actors 庫很容易模擬這種功能。希望讓 Producer 一直阻塞,直到 Consumer 接收了消息;實現(xiàn)的方法很簡單:讓 Producer 一直阻塞,直到它從 Consumer 收到已經(jīng)接收消息的確認(rèn)。從某種意義上說,這就是以前的基于監(jiān)視器的代碼所做的,那個版本通過對鎖對象使用監(jiān)視器發(fā)送這種信號。

#p#

在 Scala Actors 庫中,最容易的實現(xiàn)方法是使用 !? 方法而不是 ! 方法(這樣就會一直阻塞到收到確認(rèn)時)。(在 Scala Actors 實現(xiàn)中,每個 Java 線程都是一個 actor,所以回復(fù)會發(fā)送到與 main 線程隱式關(guān)聯(lián)的郵箱)。這意味著 Consumer 需要發(fā)送某種確認(rèn);這要使用隱式繼承的 reply(它還繼承 receive 方法),見清單 7:

清單 7. Take 2,開拍!

  1. object ProdConSample2  
  2. {  
  3.   case class Message(msg : String)  
  4.     
  5.   def main(args : Array[String]) : Unit =  
  6.   {  
  7.     val consumer =  
  8.       actor  
  9.       {  
  10.         var done = false 
  11.         while (! done)  
  12.         {  
  13.           receive  
  14.           {  
  15.             case msg =>  
  16.               System.out.println("Received message! -> " + msg)  
  17.               done = (msg == "DONE")  
  18.               reply("RECEIVED")  
  19.           }  
  20.         }  
  21.       }  
  22.       
  23.     System.out.println("Sending....")  
  24.     consumer !? "Mares eat oats" 
  25.     System.out.println("Sending....")  
  26.     consumer !? "Does eat oats" 
  27.     System.out.println("Sending....")  
  28.     consumer !? "Little lambs eat ivy" 
  29.     System.out.println("Sending....")  
  30.     consumer !? "Kids eat ivy too" 
  31.     System.out.println("Sending....")  
  32.     consumer !? "DONE"        
  33.   }  
  34. }  

如果喜歡使用 spawn 把 Producer 放在 main() 之外的另一個線程中(這非常接近最初的代碼),那么代碼可能像清單 8 這樣:

清單 8. Take 4,開拍!

  1. object ProdConSampleUsingSpawn  
  2. {  
  3.   import concurrent.ops._  
  4.  
  5.   def main(args : Array[String]) : Unit =  
  6.   {  
  7.     // Spawn Consumer  
  8.     val consumer =  
  9.       actor  
  10.       {  
  11.         var done = false 
  12.         while (! done)  
  13.         {  
  14.           receive  
  15.           {  
  16.             case msg =>  
  17.               System.out.println("MESSAGE RECEIVED: " + msg)  
  18.               done = (msg == "DONE")  
  19.               reply("RECEIVED")  
  20.           }  
  21.         }  
  22.       }  
  23.     
  24.     // Spawn Producer  
  25.     spawn  
  26.     {  
  27.       val importantInfo : Array[String] = Array(  
  28.         "Mares eat oats",  
  29.         "Does eat oats",  
  30.         "Little lambs eat ivy",  
  31.         "A kid will eat ivy too",  
  32.         "DONE" 
  33.       );  
  34.         
  35.       importantInfo.foreach((msg) => consumer !? msg)  
  36.     }  
  37.   }  
  38. }  

無論從哪個角度來看,基于 actor 的版本都比原來的版本簡單多了。讀者只要讓 actor 和隱含的郵箱自己發(fā)揮作用即可。

但是,這并不簡單。actor 模型完全顛覆了考慮并發(fā)性和線程安全的整個過程;在以前的模型中,我們主要關(guān)注共享的數(shù)據(jù)結(jié)構(gòu)(數(shù)據(jù)并發(fā)性),而現(xiàn)在主要關(guān)注操作數(shù)據(jù)的代碼本身的結(jié)構(gòu)(任務(wù)并發(fā)性),盡可能少共享數(shù)據(jù)。請注意 Producer/Consumer 示例的不同版本的差異。在以前的示例中,并發(fā)功能是圍繞 Drop 類(有界限的緩沖區(qū))顯式編寫的。在本文中的版本中,Drop 甚至沒有出現(xiàn),重點在于兩個 actor(線程)以及它們之間的交互(通過不共享任何東西的消息)。

當(dāng)然,仍然可以用 actor 構(gòu)建以數(shù)據(jù)為中心的并發(fā)構(gòu)造;只是必須采用稍有差異的方式。請考慮一個簡單的 “計數(shù)器” 對象,它使用 actor 消息傳達 “increment” 和 “get” 操作,見清單 9:

清單 9. Take 5,計數(shù)!

  1. object CountingSample  
  2.  {  
  3.    case class Incr  
  4.    case class Value(sender : Actor)  
  5.    case class Lock(sender : Actor)  
  6.    case class UnLock(value : Int)  
  7.    
  8.    class Counter extends Actor  
  9.    {  
  10.      override def act(): Unit = loop(0)  
  11.  
  12.      def loop(value: int): Unit = {  
  13.        receive {  
  14.          case Incr()   => loop(value + 1)  
  15.          case Value(a) => a ! value; loop(value)  
  16.          case Lock(a)  => a ! value  
  17.                           receive { case UnLock(v) => loop(v) }  
  18.          case _        => loop(value)  
  19.        }  
  20.      }  
  21.    }  
  22.      
  23.    def main(args : Array[String]) : Unit =  
  24.    {  
  25.      val counter = new Counter  
  26.      counter.start()  
  27.      counter ! Incr()  
  28.      counter ! Incr()  
  29.      counter ! Incr()  
  30.      counter ! Value(self)  
  31.      receive { case cvalue => Console.println(cvalue) }      
  32.      counter ! Incr()  
  33.      counter ! Incr()  
  34.      counter ! Value(self)  
  35.      receive { case cvalue => Console.println(cvalue) }      
  36.    }  
  37.  }  

#p#

為了進一步擴展 Producer/Consumer 示例,清單 10 給出一個在內(nèi)部使用 actor 的 Drop 版本(這樣,其他 Java 類就可以使用這個 Drop,而不需要直接調(diào)用 actor 的方法):

清單 10. 在內(nèi)部使用 actor 的 Drop

  1. object ActorDropSample  
  2. {  
  3.   class Drop  
  4.   {  
  5.     private case class Put(x: String)  
  6.     private case object Take  
  7.     private case object Stop  
  8.  
  9.     private val buffer =  
  10.       actor  
  11.       {  
  12.         var data = "" 
  13.         loop  
  14.         {  
  15.           react  
  16.           {  
  17.             case Put(x) if data == "" =>  
  18.               data = x; reply()  
  19.             case Take if data != "" =>  
  20.               val r = data; data = ""; reply(r)  
  21.             case Stop =>  
  22.               reply(); exit("stopped")  
  23.           }  
  24.         }  
  25.       }  
  26.  
  27.     def put(x: String) { buffer !? Put(x) }  
  28.     def take() : String = (buffer !? Take).asInstanceOf[String]  
  29.     def stop() { buffer !? Stop }  
  30.   }  
  31.     
  32.   def main(args : Array[String]) : Unit =  
  33.   {  
  34.     import concurrent.ops._  
  35.     
  36.     // Create Drop  
  37.     val drop = new Drop()  
  38.       
  39.     // Spawn Producer  
  40.     spawn  
  41.     {  
  42.       val importantInfo : Array[String] = Array(  
  43.         "Mares eat oats",  
  44.         "Does eat oats",  
  45.         "Little lambs eat ivy",  
  46.         "A kid will eat ivy too" 
  47.       );  
  48.         
  49.       importantInfo.foreach((msg) => { drop.put(msg) })  
  50.       drop.put("DONE")  
  51.     }  
  52.       
  53.     // Spawn Consumer  
  54.     spawn  
  55.     {  
  56.       var message = drop.take()  
  57.       while (message != "DONE")  
  58.       {  
  59.         System.out.format("MESSAGE RECEIVED: %s%n", message)  
  60.         message = drop.take()  
  61.       }  
  62.       drop.stop()  
  63.     }  
  64.   }  
  65. }  

可以看到,這需要更多代碼(和更多的線程,因為每個 actor 都在一個線程池內(nèi)部起作用),但是這個版本的 API 與以前的版本相同,它把所有與并發(fā)性相關(guān)的代碼都放在 Drop 內(nèi)部,這正是 Java 開發(fā)人員所期望的。

actor 還有更多特性。

在規(guī)模很大的系統(tǒng)中,讓每個 actor 都由一個 Java 線程支持是非常浪費資源的,尤其是在 actor 的等待時間比處理時間長的情況下。在這些情況下,基于事件的 actor 可能更合適;這種 actor 實際上放在一個閉包中,閉包捕捉 actor 的其他動作。也就是說,現(xiàn)在并不通過線程狀態(tài)和寄存器表示代碼塊(函數(shù))。當(dāng)一個消息到達 actor 時(這時顯然需要活動的線程),觸發(fā)閉包,閉包在它的活動期間借用一個活動的線程,然后通過回調(diào)本身終止或進入 “等待” 狀態(tài),這樣就會釋放線程。(請參見 參考資料 中 Haller/Odersky 的文章)。

在 Scala Actors 庫中,這要使用 react 方法而不是前面使用的 receive。使用 react 的關(guān)鍵是在形式上 react 不能返回,所以 react 中的實現(xiàn)必須重復(fù)調(diào)用包含 react 塊的代碼塊。簡便方法是使用 loop 結(jié)構(gòu)創(chuàng)建一個接近無限的循環(huán)。這意味著 清單 10 中的 Drop 實現(xiàn)實際上只通過借用調(diào)用者的線程執(zhí)行操作,這會減少執(zhí)行所有操作所需的線程數(shù)。(在實踐中,我還沒有見過在簡單的示例中出現(xiàn)這種效果,所以我想我們只能暫且相信 Scala 設(shè)計者的說法)。

在某些情況下,可能選擇通過派生基本的 Actor 類(在這種情況下,必須定義 act 方法,否則類仍然是抽象的)創(chuàng)建一個新類,它隱式地作為 actor 執(zhí)行。盡管這是可行的,但是這種思想在 Scala 社區(qū)中不受歡迎;在一般情況下,我在這里描述的方法(使用 Actor 對象中的 actor 方法)是創(chuàng)建 actor 的首選方法。

結(jié)束語

因為 actor 編程需要與 “傳統(tǒng)” 對象編程不同的風(fēng)格,所以在使用 actor 時要記住幾點。

首先,actor 的主要能力來源于消息傳遞風(fēng)格,而不采用阻塞-調(diào)用風(fēng)格,這是它的主要特點。(有意思的是,也有使用消息傳遞作為核心機制的面向?qū)ο笳Z言。最知名的兩個例子是 Objective-C 和 Smalltalk,還有 ThoughtWorker 的 Ola Bini 新創(chuàng)建的 Ioke)。如果創(chuàng)建直接或間接擴展 Actor 的類,那么要確保對對象的所有調(diào)用都通過消息傳遞進行。

第二,因為可以在任何時候交付消息,而且更重要的是,在發(fā)送和接收之間可能有相當(dāng)長的延遲,所以一定要確保消息攜帶正確地處理它們所需的所有狀態(tài)。這種方式會:

讓代碼更容易理解(因為消息攜帶處理所需的所有狀態(tài))。
減少 actor 訪問某些地方的共享狀態(tài)的可能性,從而減少發(fā)生死鎖或其他并發(fā)性問題的機會。
第三,actor 應(yīng)該不會阻塞,您從前面的內(nèi)容應(yīng)該能夠看出這一點。從本質(zhì)上說,阻塞是導(dǎo)致死鎖的原因;代碼可能產(chǎn)生的阻塞越少,發(fā)生死鎖的可能性就越低。

很有意思的是,如果您熟悉 Java Message Service (JMS) API,就會發(fā)現(xiàn)我給出的這些建議在很大程度上也適用于 JMS — 畢竟,actor 消息傳遞風(fēng)格只是在實體之間傳遞消息,JMS 消息傳遞也是在實體之間傳遞消息。它們的差異在于,JMS 消息往往比較大,在層和進程級別上操作;而 actor 消息往往比較小,在對象和線程級別上操作。如果您掌握了 JMS,actor 也不難掌握。

actor 并不是解決所有并發(fā)性問題的萬靈藥,但是它們?yōu)閼?yīng)用程序或庫代碼的建模提供了一種新的方式,所用的構(gòu)造相當(dāng)簡單明了。盡管它們的工作方式有時與您預(yù)期的不一樣,但是一些行為正是我們所熟悉的 — 畢竟,我們在最初使用對象時也有點不習(xí)慣,只要經(jīng)過努力,您也會掌握并喜歡上 actor。

本文來自IBMDW中國:《面向 Java 開發(fā)人員的 Scala 指南: 深入了解 Scala 并發(fā)性》。

【相關(guān)閱讀】

  1. Scala編程語言專題
  2. 從Java走進Scala:深入了解Scala并發(fā)性
  3. 從Java走進Scala:構(gòu)建計算器 結(jié)合解析器組合子和case類
  4. 從Java走進Scala:構(gòu)建計算器 解析器組合子入門
  5. 從Java走進Scala:簡單的計算器 case類和模式匹配
責(zé)任編輯:yangsai 來源: IBMDW
相關(guān)推薦

2017-12-25 11:50:57

LinuxArch Linux

2017-01-19 21:08:33

iOS路由構(gòu)建

2020-12-24 11:19:55

JavaMapHashMap

2018-06-11 15:30:12

2024-11-18 17:12:18

C#編程.NET

2019-07-09 15:23:22

Docker存儲驅(qū)動

2019-03-05 14:09:27

Docker存儲容器

2018-12-24 10:04:06

Docker存儲驅(qū)動

2017-01-06 15:13:25

LinuxVim源代碼

2010-08-10 11:31:36

路由器配置NAT

2009-04-15 09:29:07

2010-08-12 10:02:16

路由器NAT

2010-03-04 16:28:17

Android核心代碼

2015-07-27 16:06:16

VMware Thin虛擬化

2018-04-23 14:23:12

2016-11-02 18:54:01

javascript

2010-04-07 13:05:57

2011-09-05 12:36:08

路由器限速linux路由器

2025-02-08 08:21:48

Java排序Spring

2011-05-10 10:28:55

點贊
收藏

51CTO技術(shù)棧公眾號

日本h片在线看| 久久久久久青草| 欧美日本国产| 日韩精品中文字幕视频在线| 久久99爱视频| 国产很黄免费观看久久| 91亚色免费| 老汉色老汉首页av亚洲| 亚洲成人国产精品| 国产午夜精品一区理论片| 亚洲国产高清aⅴ视频| 黄频视频在线观看| 亚洲精品综合| 成人久久一区二区| 日韩高清影视在线观看| 日韩在线免费高清视频| 中国av在线播放| 在线免费一区三区| 久久久久久77777| 久久久久久久久97黄色工厂| 日本不卡久久| 国语自产精品视频在线看8查询8| 欧美在线观看日本一区| 成人国产在线| 亚洲三级免费看| 日本在线啊啊| 亚洲第一视频在线观看| 久久久久久国产精品免费无遮挡| 色综合一区二区| 在线中文av| 亚洲午夜免费视频| av最新网址| 中文字幕在线观看不卡视频| 国产 porn| 国产精品久久久久久妇女6080| 国产l精品国产亚洲区久久| 丁香亚洲综合激情啪啪综合| 国产高清免费在线| 国产毛片精品视频| 久久精品无码中文字幕| 丁香激情综合国产| 激情深爱综合网| 久久久久久免费| 亚洲成人av免费看| 国产精品情趣视频| 国产专区视频| 亚洲动漫第一页| 日本午夜在线| 91精品欧美久久久久久动漫| 国产激情视频在线| 亚洲精品在线免费观看视频| 97在线视频免费观看完整版| 亚洲国产精品va在看黑人| 人人视频精品| 久久福利视频网| 日韩丝袜视频| 97se视频在线观看| 日日夜夜免费精品| 免费看污污视频| 久久精品视频在线看| 午夜视频国产| 欧美日韩国产高清一区| 超碰在线视屏| 久久91亚洲人成电影网站| 国产精品亚洲二区| 久久久久久亚洲精品不卡4k岛国 | 欧美中文字幕不卡| 免费看a在线观看| 亚洲精品黄网在线观看| 国产资源一区| 国产成人精品久久二区二区| 欧美国产三级| 无码人妻aⅴ一区二区三区日本| 丁香五精品蜜臀久久久久99网站 | 欧美日韩成人免费视频| 国产精品久久久久久亚洲毛片| 99热在线网站| 欧美电影免费提供在线观看| 久久久久黄色| 国产精品福利在线| 久久精品首页| 欧美视频第三页| 精品久久久久人成 | 国产精品卡一卡二| 黄色片视频在线观看| 精品亚洲一区二区三区| 日韩精品福利一区二区三区| 国产一区二区不卡视频| 菠萝蜜视频在线观看一区| 成视人a免费观看视频| 欧美性高清videossexo| 东京一区二区| 国产美女91呻吟求| 国产在线麻豆精品观看| 中出福利视频| 日韩av一区在线| 激情综合网五月| 中文字幕一区二区三区最新 | 精品成人在线| r级无码视频在线观看| 欧美日韩亚洲天堂| 成人自拍视频网| 51国偷自产一区二区三区的来源| 久久国产精品一区二区| 国产天堂在线观看| 亚洲精品久久久久久久久久久久久| 久久狠狠久久| 永久免费精品视频网站| 亚洲国产精品自拍| 国产一区二区三区四区五区3d| 国产精品初高中精品久久| 久久久精品人体av艺术| 在线观看wwwxxxx| 国产精品成人免费视频| 国产成人一级电影| av电影在线观看| 57pao精品| 国产91精品露脸国语对白| 男女污污视频在线观看| 欧美xxxx综合视频| 免费成人在线影院| 日韩精品系列| www 久久久| 亚洲午夜精品久久| 日韩欧美激情在线| 亚洲国产精品成人综合| 日韩黄色小视频| 欧美精品系列| 98色花堂精品视频在线观看| 97视频免费| 黄色片网址在线观看| 国产欧美韩国高清| 亚洲精品久久久蜜桃| 色爱综合区网| 成人黄色在线免费观看| 一区二区理论电影在线观看| freemovies性欧美| 亚洲美女精品久久| 久久夜色精品国产噜噜av小说| 91原创国产| 亚洲精品久久7777777| 国产成人综合精品三级| 性生大片免费观看性| 欧美日韩视频专区在线播放| 99精品美女| 神马午夜dy888| 国产精品高潮久久久久无| 欧美精品免费看| 久久草av在线| 麻豆视频在线| 国产 高清 精品 在线 a| 亚洲国产日韩a在线播放| 香蕉一区二区| 国产男女爽爽爽| 热久久免费国产视频| 国产精品国产自产拍在线| 91精品啪在线观看国产手机| 一本久道中文无码字幕av| 美女撒尿一区二区三区| 国产婷婷色一区二区三区四区| 国产成+人+综合+亚洲欧美| 成人精品视频在线播放| 永久免费看mv网站入口亚洲| 国产在线国偷精品免费看| 蜜桃视频动漫在线播放| 国产高清免费在线| 亚洲人成五月天| 成人毛片老司机大片| 成人在线观看免费播放| 亚洲国产精品久久久久婷蜜芽 | 91xxx视频| 亚洲精品福利在线观看| 国产一区二区不卡老阿姨| 天天免费亚洲黑人免费| 国产真人做爰毛片视频直播 | 国产九九精品| 日本一级理论片在线大全| 欧美精品中文字幕一区二区| 亚洲精品一区二区三区四区高清| 另类欧美日韩国产在线| jizz久久久久久| 香蕉521av成人网| 91久久精品国产91久久性色| 欧美人与性动xxxx| 黄色精品一二区| 亚洲精品aⅴ| 又黄又爽在线观看| 日本不卡二区| 久久成人一区二区| 婷婷中文字幕综合| 三级成人在线视频| 成人51免费| 在线一级观看| 午夜精品视频在线观看一区二区 | 四虎国产精品永远| 欧美重口乱码一区二区| 一区二区在线免费视频| 中文字幕亚洲视频| 中日韩男男gay无套| 懂色av色香蕉一区二区蜜桃|