2016-05-18 29 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,因此警告。

相关问题