我在理解Java中的volatile变量时遇到了一些困难。
我有一个参数化类,它包含一个像这样的volatile变量:
public class MyClass<T> {
private volatile T lastValue;
// ... other code ...
}
我必须针对lastValue
实现某些基本操作,包括get-value-if-not-null。
这些操作是否需要同步?我可以通过以下方法逃脱吗?
public void doSomething() {
String someString;
...
if (lastValue != null) {
someString += lastValue.toString();
}
}
或者我是否需要将空检查粘贴到同步块中?
public void doSomething() {
String someString;
...
synchronized(this) {
if (lastValue != null) {
someString += lastValue.toString();
}
}
}
我知道对于像get和set这样的原子操作,我应该没有应用同步(例如public T getValue() { return lastValue; }
)。但我不确定非原子操作。
答案 0 :(得分:13)
volatile
保证可见性(其他线程可以看到一个线程所做的更改),但它不保证多个操作的原子性。
是的,lastValue
可能会在null
和if (lastValue != null)
之间变为someString += lastValue.toString();
,而您的代码可能会抛出NullPointerException
。
您可以添加同步(但是您需要同步对变量的所有写访问),或者对于该简单用例,您可以使用局部变量:
public void doSomething() {
String someString;
T lastValueCopy = lastValue;
if (lastValueCopy != null) {
someString += lastValueCopy.toString();
}
}
答案 1 :(得分:2)
这在很大程度上取决于类型T的属性。在最简单的情况下,T是一个不可变类型,即你读取的T值,它不会改变其内部状态。在这种情况下,您不需要同步,只需将引用读入局部变量并根据需要使用它。
如果T是可变类型,那么在使用它时需要防止实例更改其状态。在这种情况下,您需要特别确保T实例受其保护的同步。同步此通常不会确保,它不会阻止您从任何其他地方更改T的状态。必须通过规则来定义特定T的正确锁定(并且没有语言元素确保您不违反这些规则)。
在特殊情况下,T是可变的,你将 this 定义为正确的锁,你需要同步 - 但你不再需要它了。
那说使用volatile与synchronized同步看起来很可疑。
答案 2 :(得分:0)
并行处理不保证数据更新对其原点的引用。在这种情况下,您必须在作为参考对象操作时进行显式同步。使用synchronized,等待并通知它。
有关详细信息:http://oracle2java.blogspot.com.br/2013/12/java-sincronizar-referencia-entre.html