我对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中它说
但是,您可以指定原子操作:
- 读取和写入对于引用变量是原子的[...]
原子动作不能交错,因此可以使用它们而不用担心线程干扰。
我只是想确保我理解正确。
提前致谢:)
答案 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中的工具。*