在线程之间同步对象的最佳实践

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一个一致的对象;不是一个有一些中间状态的对象。它使并发更容易,因为状态对象是不可变的。