Java:volatile足以使类线程安全吗?

时间:2011-12-16 18:19:59

标签: java thread-safety volatile

我对Java中的volatile语句有疑问。请看这个构造的例子:

class Master {
    // Foo is a class with thread-safe methods
    public volatile Foo foo;
}

class Worker1 implements Runnable {
    protected Master master

    void run() { ... };
}

class Worker2 implements Runnable {
    protected Master master

    void run() { ... };
}

我们有2个工作线程,它们同时包含对Master类对象的引用。在他们的工作中,他们都必须访问master.foo的方法。在某些时候,master的Foo对象将被其中一个工作线程更改。现在我的问题是:使用volatile会使整个构造线程安全吗?我问这是因为在Java tutorial from Oracle中它说

  

但是,您可以指定原子操作:

     
      
  • 读取和写入对于引用变量是原子的[...]
  •   
     

原子动作不能交错,因此可以使用它们而不用担心线程干扰。

我只是想确保我理解正确。

提前致谢:)

4 个答案:

答案 0 :(得分:11)

在Java中读取和写入引用始终是原子的,因此没有危险,例如,在某种半更新状态下看到引用。如果这是“线程安全”的意思,那么无论是否使用关键字,操作都是线程安全的。

但是volatile不是关于原子性的,它会影响线程是否可以在本地缓存写入。 volatile会强制写入写回主内存并对其他线程可见。如果你的意思是线程安全,那么是的,你需要这个关键字。

volatile本身不影响foo上的方法调用是否排除其他线程。如果这就是你的意思,你想要使用synchronized,并在另一个地方。

答案 1 :(得分:4)

volatile确保读取变量的线程将在另一个线程之前看到此变量中设置的新值。它不会看到存储在处理器缓存中的陈旧值。

它是否使整个程序线程安全取决于程序的功能,它如何使用变量,以及Foo类的设计方式。

答案 2 :(得分:3)

挥发性不够。

我相信这个例子展示了一个不稳定的情况:

共享对象:

public class Blammo
{
  public String kapow;
}

主题1中的代码:

Blammo blammo = new Blammo();
blammo.kapow = "zowie";
if ((blammo.kapow != null) && (blammo.kapow.isEmpty()))
{
  ...
}

主题2中的代码:

blammo.kapow = null;

线程1正在执行并执行(blammo.kapow!= null) 线程1交换掉,线程2交换进来。
线程2执行(blammo.kapow = null) 线程2交换掉,线程1交换。
线程1现在执行(blammo.kapow.isEmpty())并抛出空指针异常。

答案 3 :(得分:1)

如果您唯一要做的就是设置此变量,那么volatile就足够了。正如评论中指出的那样,你需要使用volatile来使更改可见(但是,如果没有volatile,你将不会有任何损坏的更新,就像你可以使用long或double,见下文)。

默认情况下,读取和写入引用变量(对象的引用)是原子的,读取和写入大多数原始类型(long和double除外)。

使用volatile,您可以确保对long和double类型的变量的读取和写入也是原子的,在这种情况下您不需要。

所以,是的,在这个例子中,它已经足够了。但是,如果您计划发送更复杂的消息,请考虑使用java.util.concurrent中的工具。*