Atomically update multiple volatile and j.u.c.atom

2019-07-07 08:18发布

In order to atomically update two and more volatile variables does it need to be guarded by lock with synchronized, reentrantReadWriteLock etc?

i.e.

volatile int vVar1, vVar1; // or AtomicInteger

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

The same code is for java.util.concurrent.atomic variables.

4条回答
We Are One
2楼-- · 2019-07-07 08:42

I want to update my two variables atomically

You can't. There are no atomic operations in the Java language or in the Java standard library that span more than one variable.

You can probably solve your problem using the synchronized keyword, but using synchronized is different from using atomics because, in order for it to work, the threads must cooperate with one another.

If there is a specific relationship that must always exist between those two variables (a.k.a., an invariant), and if you can't update the variables without temporarily breaking the invariant, then you must synchronize the code that does the update, and you must also synchronize every other block of code that expects the invariant to be true.

That's because, when you write this:

synchronized(foo) { ... }

It doesn't prevent other threads from doing anything except synchronizing on the same object at the same time.


Also note: Once you have properly synchronized all access to the variables, then you won't need them to be volatile. That's because whatever one thread writes to memory before releasing a lock is guaranteed to become visible to any other thread that subsequently acquires the same lock.

查看更多
劳资没心,怎么记你
3楼-- · 2019-07-07 08:57

Create a Class which encapsulates all of your state variables, then use AtomicReference to refer to them. This alleviates race conditions where a thread needs to set/check multiple values safely.

// 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) { }

The benefit here is that Thread 2 is able to read MyState.a and MyState.b consistently from the same MyState instance, as opposed to having the MyState instance variable it's referencing change in-between checks.

查看更多
叛逆
4楼-- · 2019-07-07 09:06

An alternative would be to use a volatile array:

private volatile int[] var;

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

that would be atomic but it assumes that the rest of your code does not access vVar1 and vVar2 directly. There may be better options depending on what you are trying to achieve - for example you could create an ad hoc thread safe holder class (typically immutable) for the two ints.

查看更多
对你真心纯属浪费
5楼-- · 2019-07-07 09:08

If you need to assign two values atomically, chaning volatile int to AtomicInteger will not solve your race condition problem.

To solve your issue, you basically have two options:

  1. Make the methods updating the variables synchronized (and maybe the methods reading those variables too)
  2. Create an wrapper for your two variables, and make use of the fact that assignment is an atomic operation

Example for option 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;
}

I largely prefer option 2 since it is non blocking and allows you to cache any type of data.

查看更多
登录 后发表回答