1. 程式人生 > >討喜的隔離可變性(一)用角色實現隔離可變性

討喜的隔離可變性(一)用角色實現隔離可變性

宣告:本文是《Java虛擬機器併發程式設計》的第五章,感謝華章出版社授權併發程式設計網站釋出此文,禁止以任何形式轉載此文。

Java將OOP變成了可變性驅動(mutability-driven)的開發模式[1],而函數語言程式設計則著重強調不可變性,而這兩種極端的方式其實都是有問題的。如果每樣事物都是可變的,那麼我們就需要妥善處理可見性和競爭條件。而在一個真實的應用程式中,也並非所有事物都是不可變的。即使是純函式式語言也提供了程式碼限制區,在該區域內允許出現帶副作用的邏輯以及按順序執行這些邏輯的方法。但無論我們傾向於哪種程式設計模型,避免共享可變性都是毋庸置疑的。

共享可變性——併發問題的根源所在——是指多個執行緒可以同時更改相同的變數。而隔離可變性——一個可以消除大部分併發問題的不錯的折衷方案——是指任意時刻有且只有一個執行緒(或角色)可以訪問某個可變變數。

在OOP中,由於物件的狀態都被封裝在物件中,所以只有例項函式才能夠操作物件的狀態。然而不同的執行緒可以同時呼叫這些函式,從而導致併發問題的發生。在基於角色(actor)的程式設計模型中,我們只允許一個角色(actor)操作物件的狀態。因此,即使整個應用程式是多執行緒的,那些角色(actor)本身也都是單執行緒的,所以也就不會有任何可見性和競爭條件相關的問題。雖然角色(actor)都可以發出操作執行請求,但它們卻無法接觸到其他角色(actor)託管的可變狀態。

我們在使用基於角色(actor)的模型進行程式設計時,往往會採取與普通的面向物件程式設計不同的設計方法。即將問題拆分成若干個可計算的非同步任務,並將它們賦予不同的角色(actor)。其中,每個角色(actor)只負責執行分配給自己的那部分任務。這樣我們就可以將任意可變狀態限定在至多一個角色(actor)當中(見圖 8‑1)。此外,我們還需要保證在角色(actor)之間傳遞的訊息都是完全不可變的才行。

在這種設計方法中,我們令每個actor都負責解決問題的一個組成部分,而它們所接收的資料則都當作不可變物件來處理。一旦完成了分配給自己的任務,這些角色(actor)就會把處理結果封裝到不可變物件中並返回給呼叫角色或其他特定的後置處理(post-processing)角色。我們可以將這種方式想象為OOP進化版,即我們讓可變且活躍的物件分別執行在屬於自己的執行緒裡。在這種模式下,對物件進行操作的唯一方法是將訊息傳遞給它們而不是直接呼叫其成員函式。


[1] 在這個問題上Java還有其他同案犯,所以我們不應該把所有責任都歸咎於Java。


方 騰飛

花名清英,併發網(ifeve.com)創始人,暢銷書《Java併發程式設計的藝術》作者,螞蟻金服技術專家。目前工作於支付寶微貸事業部,關注網際網路金融,併發程式設計和敏捷實踐。微信公眾號aliqinying。