Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multiple fields: volatile or AtomicReference?

I have to synchronize access between threads to a shared object, whose state consists of several fields. Say:

class Shared{
String a; Integer b;
//constructor, getters and setters
....
}

I have possibly many threads reading this objects, doing

//readers
shared.getA();
shared.getB();

and only one thread that will write at a certain point:

//writer
shared.setA("state");
shared.setB(1);

now my question is how to ensure that reading threads won't find the shared object in an inconsistent state.

I read many answers saying that for consistency between threads volatile is the solution,but I'm not sure how it works on multiple fields. E.g., is that enough?

volatile  String a; volatile Integer b;

Another solution would be to make the shared object immutable and use AtomicReference, E.g.,

AtomicReference<Shared> shared = ....

and then the writer will just swap the reference:

Shared prev = shared.get(); 
Shared newValue = new Shared("state",1);
while (!shared.compareAndSet(prev, newValue)) 

Is that approach correct? thanks!

Update In my setting the Shared objects are retrieved from a ConcurrentHashMap<Id,Shared>, so the comments agree that the way to go is either using the immutable approach or via synchronizing the updates on shared all together. However, for completeness would be nice to know whether the solution above with the ConcurrentHashMap<Id,AtomicReference<Shared>> is viable or wrong or just superfluous. Anyone can explain? thanks!

like image 487
legrass Avatar asked Oct 01 '22 19:10

legrass


2 Answers

First of all you should make Shared immutable:

class Shared{
   private final String a; 
   private final int b;
   //constructor, getters and NO setters
}

And if you have only one writer you can safely use volatile, there is no need in AtomicRefference. At the point where information is updated old object should not be modified, but rather a new created and assigned to a volatile refference.

like image 180
Mikhail Avatar answered Oct 05 '22 12:10

Mikhail


If you need to write both A and B together to keep them consistent, e.g. they are a name and a Social Security Number, one approach is to use synchronized everywhere and write a single, combined setter.

public synchronized void setNameAndSSN(String name, int ssn) {
   // do validation checking etc...
   this.name = name;
   this.ssn = ssn;
}

public synchronized String getName() { return this.name; }

public synchronized int getSSN() { return this.ssn; }

Otherwise the reader could "see" the object with the new name but with the old SSN.

The immutable approach makes sense too.

like image 42
user949300 Avatar answered Oct 05 '22 10:10

user949300