我已经阅读了很多关于 synchronized 和 volatile 关键字/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
的想法类是提供X
在调用 X
的 setter 时,对一组(在本例中只有一个)引用值进行分类目的。问题是:
- 可以
x
仍被声明为volatile
并确保所有线程的共同可见性或需要进一步同步? - 想法是让类的所有对象
Y
不可变的。所以,我假设它的所有字段也必须是不可变的。制作Y
的最佳方法是什么?用户可实现但同时又是线程安全的?一个实现线程安全机制的抽象类,然后它可以扩展?目前,Y
是一个带有getter方法的接口(interface),可以实现,当然不是线程安全的。 - 是否从并发访问的角度正确实现了启动/停止机制?
最佳答案
问题的症结在于 private volatile Y yObj;
只会使 yObj
引用 volatile
,而不是其内容。
当您稍后执行 x = yObj.getY();
时,您可能会请求访问非 volatile 变量,这在理论上可能是线程不安全的。
使 yObj
不可变可能会有所帮助,但执行起来会很困难。
您的启动/停止机制看起来不错,但我会使用 AtomicBoolean
,放弃同步并使用 if(active.compareAndSet(false, true) { ...
或类似的。
关于java - 并发访问 : volatility and synchronization,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17901593/