2011-10-02 69 views
25

在Java中,跨多個線程(以及一般情況下)使用對象時,最好使字段成爲final。例如,Scala final vs val併發可見性

public class ShareMe { 
    private final MyObject obj; 
    public ShareMe(MyObject obj) { 
     this.obj = obj; 
    } 
} 

在這種情況下,obj的知名度將是一致的在多個線程(假設OBJ擁有所有最終場爲好),因爲它是使用final關鍵字安全標準建造。

在scala中,它沒有出現val編譯到最終引用,而是val是scala中的語義,它可以防止您重新分配變量(Scala final variables in constructor)。如果scala構造函數變量沒有被定義爲final,他們是否會遇到同樣的問題(在actors中使用這些對象時)?

回答

44

答案的另一個問題是誤導。術語final有兩個含義:a)對於Scala字段/方法和Java方法,它表示「不能在子類中重寫」,以及b)對於Java字段和JVM字節碼,這意味着「字段必須在構造函數中初始化並且不能被重新分配「。

標記爲val(或者等價地,沒有修飾符的案例類參數)的類參數在第二意義上確實是最終的,因此線程安全。

這裏的證明:

scala> class A(val a: Any); class B(final val b: Any); class C(var c: Any) 
defined class A 
defined class B 
defined class C 

scala> import java.lang.reflect._ 
import java.lang.reflect._ 

scala> def isFinal(cls: Class[_], fieldName: String) = { 
    | val f = cls.getDeclaredFields.find(_.getName == fieldName).get 
    | val mods = f.getModifiers 
    | Modifier.isFinal(mods) 
    | } 
isFinal: (cls: Class[_], fieldName: String)Boolean 

scala> isFinal(classOf[A], "a") 
res32: Boolean = true 

scala> isFinal(classOf[B], "b") 
res33: Boolean = true 

scala> isFinal(classOf[C], "c") 
res34: Boolean = false 

或者與javap,可以從REPL方便地運行:

scala> class A(val a: Any) 
defined class A 

scala> :javap -private A 
Compiled from "<console>" 
public class A extends java.lang.Object implements scala.ScalaObject{ 
    private final java.lang.Object a; 
    public java.lang.Object a(); 
    public A(java.lang.Object); 
} 
+0

感謝您的澄清。你能看到我對這個問題的更新嗎?我還看到,對於沒有修飾符的非case類,聲明爲final的構造函數變量(如javap中所示)。 –

+0

@retronym,我來到你的答案試圖找到我的問題的答案,[在Scala中的不變性和線程安全性](http://stackoverflow.com/questions/32113124/immutability-and-thread-safety-in-斯卡拉)。如您所知,Java中的'final'關鍵字也用於避免ctor指令的重定位,如[this]中所述(https://www.cs.umd.edu/~pugh/java/memoryModel /jsr-133-faq.html#finalRight)鏈接。所以,如果我已經理解了你的答案,那麼在Scala類中使用'val'字段就等於在Java類中聲明'final'字段,讓JMM正常工作? –

1

我想我可能誤解了var是如何編譯的。我創建示例類

class AVarTest(name:String) { 
    def printName() { 
    println(name) 
    } 
} 

我跑javap -private並導致

public class AVarTest extends java.lang.Object implements scala.ScalaObject{ 
    private final java.lang.String name; 
    public void printName(); 
    public AVarTest(java.lang.String); 
} 

而且名字實際上是編譯決賽。

這也顯示在Scala val has to be guarded with synchronized for concurrent access?

+2

注意,爲2.9.0.1(可能更早),你最終的唯一理由與字段名稱是因爲你在printName中引用它。如果沒有方法引用ctor參數,則不會生成一個字段。 –