以原子方式更新多个volatile和j.u.c.atomic变量

时间:2015-01-02 18:31:33

标签: java multithreading scala atomic volatile

为了原子地更新两个或更多的volatile变量,是否需要通过lock with synchronized,reentrantReadWriteLock等来保护?

volatile int vVar1, vVar1; // or AtomicInteger

/*** Needs to be updated atomically ***/
void atomicUpdate(int var1, int var2){
  vVar1 = var1;
  vVar2 = var2;
}

相同的代码适用于java.util.concurrent.atomic变量。

4 个答案:

答案 0 :(得分:5)

如果您需要以原子方式分配两个值,则将volatile int转换为AtomicInteger将无法解决您的竞争条件问题。

要解决您的问题,您基本上有两个选择:

  1. 让方法更新变量synchronized(也许是读取这些变量的方法)
  2. 为两个变量创建一个包装器,并利用赋值是原子操作的事实
  3. 选项2的示例:

    volatile Vars vars;
    void atomicUpdate(int var1, int var2) {
        vars = new Vars(var1, var2);
    }
    
    public static Vars {
        private int vVar1;  // volatile if they need to be modified
        private int vVar2;
    }
    

    我很喜欢选项2,因为它是非阻塞的,允许你缓存任何类型的数据。

答案 1 :(得分:3)

创建一个封装所有状态变量的类,然后使用AtomicReference来引用它们。这可以缓解线程需要安全地设置/检查多个值的竞争条件。

// set initial state
AtomicReference<MyState> ref = new AtomicReference<MyState>();
ref.set(new MyState("abc", "def"));

// .. Thread 1 needs to change the state:
ref.set(new MyState("xyz", "def"));

// .. Thread 2 needs to read the state (consistently):
MyState state = ref.get();
if ("test1".equals(state.a)) { }
else if ("test2".equals(state.b) { }

此处的好处是,Thread 2能够从同一个MyState.a实例一致地读取MyState.bMyState,而不是MyState个实例变量它引用了两次检查之间的变化。

答案 2 :(得分:2)

  

我想以原子方式更新我的两个变量

你做不到。 Java语言或Java标准库中没有跨越多个变量的原子操作。

您可以使用synchronized关键字解决问题,但使用synchronized与使用原子不同,因为为了使其工作,线程必须合作彼此。

如果在这两个变量(也就是不变)之间必须始终存在特定关系,并且如果不能暂时打破不变量而无法更新变量,然后您必须同步执行更新的代码,并且还必须同步每个其他需要不变量的代码块。

那是因为,当你写这篇文章时:

synchronized(foo) { ... }

除了同时在同一个对象上同步外,它不会阻止其他线程做任何事情。


另请注意:一旦您正确同步了对变量的所有访问权限,您就不需要它们volatile。这是因为无论一个线程在释放锁之前写入内存,都可以保证对随后获取相同锁的任何其他线程可见。

答案 3 :(得分:1)

另一种方法是使用易失性数组:

private volatile int[] var;

void update (int var1, int var2) {
  var = new int[] { var1, var2 };
}

这将是原子的,但它假定您的其余代码不直接访问vVar1和vVar2。根据您要实现的目标,可能有更好的选项 - 例如,您可以为两个整数创建一个临时线程安全持有者类(通常是不可变的)。