作者:搖擺少年夢
scala并發程式設計簡介
scala actor并發程式設計模型
react模型
actor的幾種狀态
actor深入使用解析
在java中,要編寫一個線程安全的程式并不是一件易事,例如:
上面這段java代碼雖然方法前面加了synchronized ,但它仍然不是線程安全的,例如,在執行下面兩個語句
時,有可能account.incrementbalance()執行完成後,其它線程可能會擷取對象的鎖,修改account的balance,進而造成得不到預期結果的問題。解決問題的方法是将兩個功能結合起來形成一個方法:
但這可能并不是我們想要的,每次擷取balance都要将balance增加, 這顯然與實際不符。除此之外,java中的并發程式設計可能還會經常遇到死鎖問題,而這個問題往往難調試,問題可能會随機性的出現。總體上來看,java的并發程式設計模型相對較複雜,難以駕馭。
scala很好地解決了java并發程式設計的問題,要在scala中進行并發程式設計,有以下幾種途徑可以實作:
1 actor消息模型、akka actor并發模型。
2 thread、runnable
3 java.util.concurennt
4 第三方開源并發架構如netty,mina
在上述四種途徑當中,利用 actor消息模型、akka actor并發模型是scala并發程式設計的首先,本節主要介紹actor消息模型,akka actor并發模型我們将放在後面的章節中介紹。
在scala中,通過不變對象來實作線程安全,涉及到修改對象狀态時,則建立一個新的對象來實作,如:
通過不變對象實作并發程式設計,可以簡化程式設計模型,使并發程式更容易現實和控制。
java中的并發主要是通過線程來實作,各線程采用共享資源的機制來實作程式的并發,這裡面臨競争資源的問題,雖然采用鎖機制可以避免競争資源的問題,但會存在死鎖問題,要開發一套健壯的并發應用程式具有一定的難度。而scala的并發模型相比于java它更簡單,它采用消息傳遞而非資源共享來實作程式的并發,消息傳遞正是通過actor來實作的。下面的代碼給出了actor使用示例
下面給的是recieve方法的部分源代碼
從上述代碼中不能看出,receive方法接受的參數是一個偏函數,并且是通過mailbox來實作消息的發送與接收。
在前述的class actordemo中,receive方法的參數為
該代碼塊在執行時被轉換為一個partialfunction[any, r]的偏函數,其中r是偏函數的傳回類型,對應case 語句=> 右邊的部分,在本例子中r是unit類型,而any對應的則對應case語句的模式部分。
前面給的是通過extends actor的方式來建立一個actor類,其實scala.actors.actor中提供了一個actor工具方法,可以非常友善地直接建立actor對象如:
上述代碼建立的actor對象無需調用start方法,對象建立完成後會立即執行。
scala中本地線程也可用作actor,下面的代碼示範了如何在repl指令行中将本地線程當作actor;
上述代碼中,如果發送的消息不是string類型的,線程将被阻塞,為避免這個問題,可以采用receivewithin方法,
scala中的actor也是建構在java線程基礎之上的,前面在使用actor時都是通過建立actor對象,然後再調用act方法來啟動actor。我們知道,java中線程的建立、銷毀及線程間的切換是比較耗時的,是以實際中盡量避免頻繁的線程建立、銷毀和銷毀。scala中提供react方法,在方法執行結束後,線程仍然被保留。下面的代碼示範了react方法的使用:
從上述代碼中可以看到,通過在react方法執行結束時加入act方法,方法執行完成後沒有被銷毀,而是繼續試圖從郵箱中擷取資訊,擷取不到則等待。
actor有下列幾種狀态:
初始狀态(new),actor對象被建立,但還沒有啟動即沒有執行start方法時的狀态
執行狀态(runnable),正在執行時的狀态
挂起狀态(suspended),在react方法中等待時的狀态
時間點挂起狀态(timedsuspended),挂起狀态的一種特殊形式,reactwithin方法中的等待時的狀态
阻塞狀态(blocked),在receive方法中阻塞等待時的狀态
時間點阻塞狀态(timedblocked),在receivewithin方法中阻塞等待時的狀态
結束狀态(terminated),執行完成後被銷毀
1 receive方法單次執行:
上述代碼隻會輸出:
i won’t do it for less than $1 million!
即後面發送的消息如:
badactor ! speak(“do ya feel lucky, punk?”)
badactor ! gesture(“face”, “grimaces”)
badactor ! speak(“well, do ya?”)
不會被處理。這是因為receive方法的單次執行問題。
2 能夠處理多個消息的receive方法:
3 actor後面實作原理仍然是線程的證據
執行結果如下:
從上述執行結果可以看到,actor最終的實作仍然是線程,隻不過它提供的程式設計模型與java中的程式設計模型不一樣而已。
4 利用!?發送同步消息,等待傳回值
代碼執行結果:
通過上述代碼執行結果可以看到,!?因為是同步消息,發送完傳回結果後才會接着發送下一條消息。
5 spawn方法發送消息
6 !! 發送異步消息,傳回值是 future[any]
執行結果:
通過上述代碼的執行結果可以看到,!!的消息發送是異步的,消息發送後無需等待結果傳回便執行下一條語句,但如果要擷取異步消息的傳回值,如:
則執行到這兩條語句的時候,程式先被阻塞,等獲得結果之後再發送其它的異步消息。
7 loop方法實作react
添加公衆微信号,可以了解更多最新spark、scala相關技術資訊
