我该将java.util.concurrent.ConcurrentLinkedQueue引用声明为volatile吗?

5

我正在使用一个java.util.concurrent.ConcurrentLinkedQueue对象在线程之间传递数据。

我应该将我的引用声明为volatile吗?


1
值得注意的是,对象在执行过程中不会改变,因此也许使用 final 更为合适? - Jared Lindsey
1
将队列设置为volatile并不会使队列内部的引用变为volatile。 - Sami Korhonen
1
volatilefinal 一样,不会对所引用的对象产生任何影响,只会影响引用本身。只有在计划替换整个队列对象时才需要将其设置为 volatile - Peter Lawrey
3个回答

5
简而言之,不会改变。
你的队列变量所包含的值是指向队列的引用。除非像这样重新分配队列:myQueue = otherQueue;否则这个值不会改变。如果你创建了队列后所做的所有操作都只是往里面添加和取出东西,那么线程是否有缓存的值并不重要,因为这个值(指向队列的引用)永远不会改变。
最好将所有变量声明为final,除非你需要对其进行修改。

完美。这非常有帮助。我不会更改我的引用(就像你说的那样),所以这很有意义。 - Jared Lindsey
1
这并不完全正确。当变量首次被赋值时,即使它被声明为“final”,其值也会发生改变。它从null变成了其他值。但是,只有在构造函数和任何实例初始化程序执行完成之前,如果该值对任何其他线程可观察到才会产生影响。 - Robin Green
@Robert Green:构造函数不能并发执行,对吗?在它返回之前,没有任何东西可以看到这个对象,对吗? - Sibbo
1
@Sibbo JLS没有规定构造函数是否可以在对象发布之前完成。话虽如此,这可能是因为在构造函数中已经声明的字段可能尚未发布到其他处理器/线程,所以看起来构造函数还没有完成。 - John Vint
@Sibbo "无法看到对象" - 错误。这就是为什么双重检查锁定单例模式会利用volatile实例,因为另一个线程可能会看到对尚未完全初始化的构造对象的引用,而volatile可以解决此问题。但是,如果字段是final,则有一些保证,但不确定这些保证是否适用于所有JVM实现。 - Vadim Kirilchuk

1
不会,因为您一直在使用同一个队列。易挥发意味着内存中变量的值对于每个处理器始终相同。请注意,即使将变量声明为易挥发,存储在寄存器中的变量也不会同步。

3
因此,“volatile”适用于引用,并且在引用可能发生更改的情况下使用? - Jared Lindsey
3
可以。它还可以用于原始类型(例如int、double等)的并发读写。 - Sibbo

1
如果可以确定它是最终的,请将其声明为final。如果不能确定,则将其声明为volatile。
尽管将其声明为volatile对队列的内部结构没有影响,但只有在分配new ConcurrentLinkedQueue()时才会添加额外的同步。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接