我已经阅读了很多关于同步和易失性关键字/idom的文章,我想我正确地理解了它们是如何工作的,以及何时应该使用它们。不过,我还是对我要做的事有些怀疑。请考虑以下几点:
public class X {
private volatile int x;
public X(int x) {
this.x = x;
}
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
}
上面的一个非常直,线程安全。现在考虑相同的X类,并进行以下更改:
public class X {
private volatile int x;
private volatile Y yObj;
private volatile boolean active;
public X(Y yObj) {
this.yObj = yObj;
active = false;
x = yObj.getY();
}
public void setX(int x) {
if (active) throw new IllegalStateException()
if (!yObj.isValid(x)) throw new IllegalArgumentException();
this.x = x;
}
public void setY(Y yObj) {
if (active) throw new IllegalStateException();
this.yObj = yObj;
x = yObj.getY();
}
public int getX() {
return x;
}
public Y getY() {
return yObj;
}
public synchronized void start() {
if (active) throw new IllegalStateException();
/*
* code that performs some initializations and condition checking runs here
* does not depend on x and yObj
* might throw an exception
*/
active = true;
}
public synchronized void stop() {
if (!active) throw new IllegalStateException();
/* some code in the same conditions of the comments in the start()
* method runs here
*/
active = false;
}
public boolean isActive() {
return active;
}
}
现在,我将
yObj
声明为volatile
,以确保每个线程在通过调用setY(Y)
方法进行更改时看到相同的对象引用。类的思想是在调用Y
对象的setter时提供X
类的一组引用值(在本例中仅为一个)。问题是:是否仍可以将
X
声明为x
并确保所有线程的公共可见性,或者需要进一步同步?其思想是使类的所有对象都是不可变的。所以,我假设它的所有字段都必须是不可变的。使
volatile
用户可实现但同时又具有线程安全性的最佳方法是什么?一个抽象类,它实现了线程安全机制,然后它就可以扩展了?目前,Y
是一个带有getter方法的接口,当然,它不能实现线程安全性。从并发访问的角度来看,启动/停止机制是否正确实现?
最佳答案
问题的关键在于private volatile Y yObj;
只会使yObj
引用volatile
,而不是其内容。
当您稍后执行x = yObj.getY();
时,您可能会请求访问一个非易失性变量,理论上这可能会导致线程不安全。
使yObj
不可变可能会有帮助,但执行起来会很困难。
你的启动/停止机制看起来不错,但我会使用一个AtomicBoolean
,放下同步并使用if(active.compareAndSet(false, true) { ...
或类似的。
关于java - 并发访问:波动性和同步,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17901593/