简体   繁体   English

多个字段:volatile还是AtomicReference?

[英]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. 我读过很多答案,它们说,为了确保线程之间的一致性, volatile是解决方案,但是我不确定它如何在多个字段上工作。 Eg, is that enough? 例如,够了吗?

volatile  String a; volatile Integer b;

Another solution would be to make the shared object immutable and use AtomicReference, Eg, 另一个解决方案是使共享对象不可变,并使用AtomicReference,

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. 更新在我的设置中,共享对象是从ConcurrentHashMap<Id,Shared>中检索的,因此注释同意,前进的方法要么是使用不变方法,要么是通过同步所有共享上的更新。 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. 但是,为了完整起见,很高兴知道上面使用ConcurrentHashMap<Id,AtomicReference<Shared>>的解决方案是可行的还是错误的,或者仅仅是多余的。 Anyone can explain? 有人可以解释吗? thanks! 谢谢!

First of all you should make Shared immutable: 首先,您应该使Shared不可变:

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. 而且,如果只有一位编写者,则可以安全地使用volatile,因此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. 在更新信息时,不应修改旧对象,而应创建新对象并将其分配给易失性引用。

As @Mikhail says in his answer, making Shared immutable and replacing the whole object is a nice approach. 就像@Mikhail在回答中所说的那样,使Shared不可变并替换整个对象是一种不错的方法。 If you don't want to or "can't" use that approach for some reason, you can just make sure all the fields on Shared are protected by the same lock, and that they are only ever modified together (see update in my example), then it is impossible for them to be seen in an inconsistent state. 如果你不想或者“不能”的使用,由于某种原因的方法,你可以确保所有的字段Shared是由同一个锁保护,而且他们永远只能修改在一起(见update我的例如),那么就不可能以不一致的状态看到它们。

eg 例如

class Shared {
  private String a;
  private String b;
  public synchronized String getA() {
    return a;
  }
  public synchronized String getB() {
    return b;
  }
  public synchronized void update(String a, String b) {
    this.a = a;
    this.b = b;
  } 
}

If you need to write both A and B together to keep them consistent , eg they are a name and a Social Security Number, one approach is to use synchronized everywhere and write a single, combined setter. 如果您需要将A和B都写在一起以保持它们的一致性 ,例如它们是一个名称和一个社会保险号,则一种方法是在各处使用synchronized并编写单个组合的设置器。

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. 否则,读者可能会“看到”具有新名称但带有旧SSN的对象。

The immutable approach makes sense too. 不变的方法也很有意义。

Marking fields volatile or making methods synchronised is not going to ensure atomicity. 标记字段易变或使方法同步不会确保原子性。

Writer should take care of the atomicity. 作家应注意原子性。

Writer should call all the setters(that should be updated atomically) inside a synchronized block. 编写器应调用同步块内的所有设置器(应自动更新)。 synchronized(shared) { shared.setA() shared.setB() ... } 同步(共享){shared.setA()shared.setB()...}

For this to work all the getters in the shared object should be synchronized too. 为此,共享对象中的所有吸气剂也应同步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM