2016-05-18 33 views
3

我不確定我在這裏正確使用@volatile。我有一個緩衝區,像這樣:@volatile用法不清楚 - 用`var`向另一個線程發送一個對象

final class BufD(val buf: Array[Double], @volatile var size: Int) 

哪些在進程之間發送,從而它可能跨越線程邊界。在發送之前,發件人可能會更新size字段。因此,我想確保接收器在任何情況下都不會在此處看到陳舊的size值。 第一個問題:@volatile確保這個或者是多餘的?

現在我介紹一個特點:

trait BufLike { 
    @volatile var size: Int 
} 

final class BufD(val buf: Array[Double], @volatile var size: Int) extends BufLike 

這給了我一個編譯器警告:

警告:(6,4)對方法尺寸標註沒有有效的目標 - 它被丟棄沒用過。您可以使用元註釋指定目標,例如@(揮發性@Getter)

@volatile var size: Int 
^

第二個問題:我應該刪除@volatile這裏或改變它以不同的方式?

+1

你爲什麼不只是使用一成不變的案例類+'.copy()'? – Tair

+1

示例:http://alvinalexander.com/scala/scala-case-class-copy-method-example – Tair

+0

@tair因爲這是高速DSP代碼,我寧願儘可能避免不必要的分配。 –

回答

4

我假設線程A創建,更新,然後將對象X傳遞給線程B.如果object-X以及直接或傳遞(字段)引用的內容不會被線程A進一步更新,那麼volatile是多餘的。接收線程的對象X狀態的一致性由JVM保證。

換句話說,如果對於object-X的邏輯所有權從線程A傳遞到線程B,那麼volatile沒有意義。相反,在現代多核系統中,volatile的性能影響可能比不變的case類留下的線程本地垃圾的性能影響更大。

如果object-X應該被共享寫入,則使字段volatile有助於共享其值,但是您將面臨另一個問題:對象-X上的非原子更新,如果字段的值取決於彼此。

正如@alf所指出的,受益於發生在之前的保證,對象必須安全地通過!這可以使用java.util.concurrent.**類來實現。像Akka這樣的高層次結構定義了他們自己的安全「傳遞」對象的機制。

參考文獻:

https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

+2

嚴格地說,_'volatile'是冗餘的,__一致性...由JVM保證。_僅當對象在線程之間安全傳遞時,即線程A「傳遞」對象的時刻,無論它可能表示什麼,_happens-before_(通過JVM定義)在線程B開始讀取它的時刻。在大多數情況下,您可以依靠現有的同步,例如阻塞隊列或原子引用。沒有現有的機制,你需要建立你自己的機制,而'volatile'可以是這樣一種機制(儘管我不推薦它)。 – alf

+0

@alf感謝您的補充評論。我現在收到來自Akka作者(用於傳遞對象的框架)的說明,確實發生了這種情況 - 保證措施在此處適用。 –

+0

在akka BTW的情況下,你不應該真的假設你在同一個JVM上,這使得另一個支持不可變對象的論點。 – alf

3

由於@tair指出,真正解決問題的方法是使用一個不變的情況下類:

發送者可以只發送出去之前更新大小字段。

看來,接收器確實是不是更新大小;如果已經發送了BufD,發件人也不會更新大小。因此,出於所有實際的原因,接收者最好接收不可變對象。

至於@volatile,它確保可見性 - 寫入確實觸及主存儲器,而不是緩存在線程本地緩存中,並且讀取包含內存屏障以確保讀取的值不過時。

如果沒有@volatile,接收者線程可以自由緩存值(它不是volatile的,因此不應該從其他線程改變,因此緩存是安全的)並且重用它而不是引用主存儲器。 (SLS 11.2.1JLS §8.3.1.4

@volatile標記,其可改變節目的控制以外的值的字段;這相當於Java中的volatile修飾符。

volatile字段(§8.3.1.4)之前發生該字段的每個後續的讀取。

這裏的問題是,要麼你並不需要所有的對象是有效不可變的(和你是一個正確的不可變一個更好),或者你想看到bufsize協調變化收件人大小。在後一種情況下,如果作者追加(而不是覆蓋!)到buf,然後更新size,則@volatile可能很有用(同時很脆弱)。在這種情況下,寫信給bufhappens-beforesize,這反過來又之前發生讀者可以從size讀取更新後的值(波動性),因此,如果讀者檢查和重新檢查的大小,和作家只有追加,你大概很好。話雖如此,我不會使用這種設計。

至於警告,它全部編譯爲Java,即JVM,字節碼,並且volatile字段的JVM標誌。特徵不能定義一個字段 - 它們只定義方法,並由擴展類決定它是一個適當的變量還是(一對)方法(SLS 4.2)。

變量聲明var x: T相當於兩者getter函數x的聲明和一個setter函數x_=

def x: T 
def x_= (y: T): Unit 

函數不能是@volatile,因此警告。

相關問題