在线程之间同步对象的最佳实践
Best practice for synchronizing an object between threads
我基本上有一个对象,它存储具有速度等附加属性的项目的 3D 位置。该位置每 100 毫秒由一个单独的线程计算一次。另一个线程访问这些属性并修改其中的一些。
模拟线程
// ...
double z = location.getZ();
// calculate new position
location.setZ(z);
// ...
其他线程
// ...
while(location.getZ() > 10); // Some busy waiting for demonstration
location.setSpeed(10);
// ...
我的第一个想法是对每个属性简单地使用 volatile
,但据我所知,对 volatile 属性的每个操作都必须是原子的。由于在某些情况下允许两个线程更改某些属性(如速度),这似乎不起作用。
我的下一个想法是使用 java 的 synchronized
关键字并同步每个 getter 和 setter 结束整个模拟周期。
// Location.java
public synchronized double getZ() { /* ... */ }
// Simulation loop
synchronized(location) {
// calculations are done here
}
现在我想知道这是正确的方法还是有更好的方法。
在 synchronized
块中调用 getZ()
是否可以,或者这个结果是否需要单独的锁?
您使用 synchronized 的方法看起来是正确的。 Volatile 确保与同一变量的下一次读取存在先行关系。但它没有建立原子性。
您可以使用 synchronize 关键字来确保 happens-before 和原子性都能正常工作。只需确保在 getter 和 setter 方法中锁定同一个监视器对象即可。
Getter方法也需要同步。否则 getter 方法可以从本地 CPU 缓存中读取变量值,而不是从主内存中读取变量值,因为 属性 未建立。所以一些其他线程可能已经使用同步 setter 更新了主内存中的值,但是 getter 仍在从 CPU 缓存中读取陈旧的缓存值。
另一种方法是在更新对象时替换整个对象。
例如
class State{
final int x,y;
... constructor + getters
}
volatile State state;
public void update(int x, int y){
this.state = new State(x,y);
}
public void useState(){
State s = state;
print(s.x+" "+s.y);
}
最大的好处是你总是可以return一个一致的对象;不是一个有一些中间状态的对象。它使并发更容易,因为状态对象是不可变的。
我基本上有一个对象,它存储具有速度等附加属性的项目的 3D 位置。该位置每 100 毫秒由一个单独的线程计算一次。另一个线程访问这些属性并修改其中的一些。
模拟线程
// ...
double z = location.getZ();
// calculate new position
location.setZ(z);
// ...
其他线程
// ...
while(location.getZ() > 10); // Some busy waiting for demonstration
location.setSpeed(10);
// ...
我的第一个想法是对每个属性简单地使用 volatile
,但据我所知,对 volatile 属性的每个操作都必须是原子的。由于在某些情况下允许两个线程更改某些属性(如速度),这似乎不起作用。
我的下一个想法是使用 java 的 synchronized
关键字并同步每个 getter 和 setter 结束整个模拟周期。
// Location.java
public synchronized double getZ() { /* ... */ }
// Simulation loop
synchronized(location) {
// calculations are done here
}
现在我想知道这是正确的方法还是有更好的方法。
在 synchronized
块中调用 getZ()
是否可以,或者这个结果是否需要单独的锁?
您使用 synchronized 的方法看起来是正确的。 Volatile 确保与同一变量的下一次读取存在先行关系。但它没有建立原子性。
您可以使用 synchronize 关键字来确保 happens-before 和原子性都能正常工作。只需确保在 getter 和 setter 方法中锁定同一个监视器对象即可。
Getter方法也需要同步。否则 getter 方法可以从本地 CPU 缓存中读取变量值,而不是从主内存中读取变量值,因为 属性 未建立。所以一些其他线程可能已经使用同步 setter 更新了主内存中的值,但是 getter 仍在从 CPU 缓存中读取陈旧的缓存值。
另一种方法是在更新对象时替换整个对象。
例如
class State{
final int x,y;
... constructor + getters
}
volatile State state;
public void update(int x, int y){
this.state = new State(x,y);
}
public void useState(){
State s = state;
print(s.x+" "+s.y);
}
最大的好处是你总是可以return一个一致的对象;不是一个有一些中间状态的对象。它使并发更容易,因为状态对象是不可变的。