Java 我应该声明字段为volatile吗?

Java 我应该声明字段为volatile吗?,java,concurrency,reference,volatile,Java,Concurrency,Reference,Volatile,考虑以下代码: class Foo { java.util.Timer timer = new java.util.Timer(); void doAction() { ... timer.schedule(new SomeTimerTask(), 0L); ... } void cancelAction() { timer.cancel(); } } 方法是从不同的线程调用的。首先调用

考虑以下代码:

class Foo {
    java.util.Timer timer = new java.util.Timer();

    void doAction() {
        ...
        timer.schedule(new SomeTimerTask(), 0L);
        ...
    }

    void cancelAction() {
        timer.cancel();
    }
}
方法是从不同的线程调用的。首先调用方法doAction()


我是否应该声明另一个线程可以看到
timer
字段
volatile

最好在
doAction()
cacelAction()
等方法上使用
synchronized
关键字,这些方法操作实例变量的关键状态
计时器


volatile
关键字的作用与您描述的将volatile字段反映到每个线程的作用非常相似,但是仅适用于每个单独的操作,而不是所有的操作。

即使没有将字段声明为volatile,它也将对所有线程可见。与变量相关联的
volatile
关键字将指示java运行时,该变量可能会被多个线程修改,因此不应在本地缓存。

您不需要将字段设置为volatile,因为两个线程都不对字段本身进行更改:其值在初始值设定项中设置一次,在那之后,它不会改变


您可能需要将同步添加到方法中,但是在这种情况下声明变量
volatile
是完全不必要的:使用
final
更合适。

不需要将字段设置为
volatile
,但最好使用
synchronized
方法

public void synchronized doAction(){}

public void synchronized cancelAction(){}

Timer类是线程安全的

因此,将
timer
声明为
volatile
就足够了

但是,如果未将
timer
分配到任何其他位置(看起来是这样),更好的解决方案是将字段声明为
final
。这足以确保可以从多个线程安全地使用
timer
变量,而无需进一步同步。(这一点由以下机构明确保证。)


如果计时器不是线程安全的,那么您需要在同步方法或块(或使用锁等实现的等效方法)中对计时器实例执行所有操作。将
计时器
声明为
volatile
final
是不够的。

@RuslanZagirov volatile允许同时访问线程,而无需进行任何锁定,但synchronize关键字通过获得对象上的锁来锁定资源。。。现在没有其他线程可以访问此资源,除非获得对象锁的线程释放它…此时另一个线程获得锁…现在此线程可以看到由前一个线程更改的字段值…但是在本例中,计时器上的操作不需要在
synchronized
块中执行,因为javadoc声明它们是线程安全的。据我所知,如果需要为其他线程传播其更新,则任何字段都应该声明为volatile。如果没有volatile关键字,所有与对象交互的线程都可以看到对象字段,但某些线程可能会在本地缓存字段值。正确吗?@RuslanZagirov-您的理解是不正确的,因为
volatile
不是唯一的方法。@RuslanZagirov即使
timer
的值在本地缓存,本地缓存的所有副本都将“指向”相同的
timer
对象实例;由于该值没有更改,因此可以缓存引用一次,并且不再触摸它。@dasblinkenlight-不正确,请参阅JLS示例17.5-1。。。在我的答案链接的页面上。您必须将该字段声明为
final
volatile
或从同步块中访问它,等等。即使该字段在初始化后从未更改过。@StephenC这很有意义-在这种情况下,使用
final
更合适。谢谢若我们从一个线程更新计时器字段,则需要volatile字段为其他线程传播更新。默认情况下,未更新时,所有线程都可以使用精确的字段值。对吗?不对。这不是唯一的方法。。。即使
计时器
已更新。(5个答案中有4个明确说明了这一点,第5个答案并不矛盾!)+1我检查了计时器代码和
schedule()
cancel()
方法是同步的,因此没有必要添加外部同步。