2011-07-29 141 views
19

我想知道是否有任何'簡單'的方法來安全地更新不可變的scala集合。考慮以下代碼:線程安全地更新Scala集合

class a { 
    private var x = Map[Int,Int]() 

    def update(p:(Int,Int)) { x = x + (p) } 
} 

此代碼不是線程安全的,正確的?我的意思是,如果我們有兩個線程調用update方法,並且讓x表示包含{1 => 2}的map並且線程A調用update((3,4))並且只管理執行x +(p)部分代碼。然後重新調度發生,線程B調用更新((13,37))併成功更新變量x。線程A繼續並結束。

畢竟這完成後,值x將等於包含{1 => 2,3 => 4}的地圖,是正確的嗎?而不是所需的{1 => 2,3 => 4,13 => 37}。有沒有簡單的方法來解決這個問題?我希望它是難以置信的,我問:)

順便說一句,我知道有像Akka STM的解決方案,但我寧願不使用這些,除非必要。

非常感謝任何答案!

編輯:此外,我寧願解決方案沒有鎖定。 Eeeew :)

+1

使用Akka STM http://akka.io/docs/akka/1.1.3/ scala/stm.html – Phil

+0

+!對於Eeeew! :D – Daniel

回答

18

就你的情況而言,正如Maurício寫道的,你的集合已經是線程安全的,因爲它是不可變的。唯一的問題是重新分配var,這可能不是一個原子操作。對於這個特定的問題,最簡單的選擇是使用java.util.concurrent.atomic中的nice類,即AtomicReference

import java.util.concurrent.atomic.AtomicReference 

class a { 
    private val x = new AtomicReference(Map[Int,Int]()) 

    def update(p:(Int,Int)) { 
    while (true) { 
     val oldMap = x.get // get old value 
     val newMap = oldMap + p // update 
     if (x.compareAndSet(oldMap, newMap)) 
     return // exit if update was successful, else repeat 
    } 
    } 
} 
+1

看起來像更簡單的同步整個塊。我的意思是,總而言之,鎖定操作會造成內存障礙... – tuxSlayer

+1

同步可能會更簡單,但這是安全的,不需要任何同步,並且可以說更有效率,除非映射更新頻繁。 –

+1

在熱插拔操作中使用var並進行同步實際上佔用資源更少。看到這個:[鏈接](http://twitter.github.io/scala_school/concurrency.html) - 「這是否花費什麼?」部分 –

3

集合本身是線程安全的,因爲它沒有共享可變狀態,但是你的代碼不是,並且沒有鎖定就無法解決這個問題,因爲你確實有共享可變狀態。您最好的選擇是鎖定方法本身,將其標記爲已同步。

另一種解決方案是使用可變的併發映射,可能是java.util.concurrent.ConcurrentMap

0

Re。讓 - 菲利普·佩萊的回答是:你可以讓這一點更可重用:

def compareAndSetSync[T](ref: AtomicReference[T])(logic: (T => T)) { 
    while(true) { 
    val snapshot = ref.get 
    val update = logic(snapshot) 
    if (ref.compareAndSet(snapshot, update)) return 
    } 
} 

def compareSync[T,V](ref: AtomicReference[T])(logic: (T => V)): V = { 
    var continue = true 
    var snapshot = ref.get 
    var result = logic(snapshot) 
    while (snapshot != ref.get) { 
    snapshot = ref.get 
    result = logic(snapshot) 
    } 
    result 
} 
+0

同樣,http://blog.scala4java.com/2012/03/atomic-update-of-atomicreference.html 看起來像一個很常見的用例。 – experquisite