简体   繁体   English

为什么我需要对volatile上的多个线程使用同步?

[英]Why do I need to use synchronized for multiple threads over volatile?

Some people says if multiple threads are reading/writing then you need to use synchronized and if one thread is reading/writing and another one is only reading then you must use volatile. 有人说,如果有多个线程在读/写,那么您需要使用同步,如果一个线程在读/写,而另一个仅在读,那么您必须使用volatile。 I don't get the difference between this situations. 我看不出这两种情况之间的区别。

Basically, the value of a volatile field becomes visible to all readers (other threads in particular) after a write operation completes on it. 基本上,对所有读取器(尤其是其他线程)而言,易失性字段的值在其上完成写操作之后变为可见。

Then If I define a variable as volatile, first threadA will read its value, threadA will update its value and write it to memory.After that variable will become visible to threadB. 然后,如果我将变量定义为volatile,首先线程A将读取其值,线程A将更新其值并将其写入内存,然后该变量将对线程B可见。 Then why do I need to synchronized block? 那为什么我需要同步阻止?

Some people says if multiple threads are reading/writing then you need to use synchronized and if one thread is reading/writing and another one is only reading then you must use volatile. 有人说,如果有多个线程在读/写,那么您需要使用同步,如果一个线程在读/写,而另一个仅在读,那么您必须使用volatile。 I don't get the difference between this situations. 我看不出这两种情况之间的区别。

There really isn't a hard and fast rule with this. 确实没有硬性规定。 Choosing whether or not to use synchronized or volatile has more to do with how the objects are being updated as opposed to how many readers or writers there are. 选择是否要使用synchronizedvolatile有更多的事情要做怎样 ,而不是许多读者或作家怎么有被更新的对象。

For example, you can achieve multiple readers and writers with an AtomicLong which wraps a volatile long . 例如,您可以使用AtomicLong来实现多个读者和作家, AtomicLong包装了一个volatile long

  private AtomicLong counter = new AtomicLong();
  ...
  // many threads can get/set this counter without synchronized
  counter.incrementAndGet();

And there are circumstances where you would need a synchronized block even with a single reader/writer. 在某些情况下,即使只有一个读取器/写入器,您也需要一个synchronized块。

synchronized (status) {
   status.setNumTransactions(dao.getNumTransactions());
   // we don't want the reader thread to see `status` partially updated here
   status.setTotalMoney(dao.getTotalMoney());
}

In the above example, since we are making multiple calls to update the status object we may need to ensure that other threads don't see it when the num-transactions has been updated but not the total-money. 在上面的示例中,由于我们要进行多次调用来更新status对象,因此我们可能需要确保在更新num-transactions而不是total-money时其他线程看不到它。 Yes, AtomicReference handles some of these cases but not all. 是的, AtomicReference处理其中一些情况,但并非全部。

To be clear, marking a field volatile ensures memory synchronization. 明确地说,将字段标记为volatile可确保内存同步。 When you read a volatile field you cross a read memory barrier and when you write it you cross a write memory barrier. 当您读取一个volatile字段时,您会越过读存储屏障,而在写入时,您会越过写存储屏障。 A synchronized block has a read memory barrier at the start and a write barrier at the end of the block and is has mutex locking to ensure only one thread can enter the block at once. synchronized块在块的开始处具有读取内存屏障,在块的末尾具有写入屏障, 并且具有互斥锁以确保只有一个线程可以立即进入该块。

Sometimes you just need memory barriers to achieve proper sharing of data between threads and sometimes you need locking. 有时您只需要内存屏障即可在线程之间实现正确的数据共享,有时则需要锁定。

As comments suggest, you might do some further reading. 正如评论所建议的,您可能需要进一步阅读。 But to give you an idea you can take a look at this stackoverflow question and think for example about the following scenario: 但是,为了给您一个想法,您可以看一下这个stackoverflow问题,并考虑以下情形的示例:

You have couple of variables which need to be in the right state. 您有几个变量需要处于正确的状态。 But although you make them all volatile you need time to update them by some code executing. 但是,尽管您使它们均易失,但仍需要时间通过执行一些代码来更新它们。

Exactly this code may be executed almost at the same time by a different thread. 确切地说,该代码几乎可以同时由不同的线程执行。 The first variables could be "OK" and somehow synchronized but some other maybe dependent on the first ones and are not correct yet. 第一个变量可以是“ OK”并以某种方式同步,但其他一些变量可能取决于第一个变量,尚不正确。 Thus you need a synchronized block in that case. 因此,在这种情况下,您需要一个同步块。

To add one more post for further reading about volatile look here 要添加更多帖子以进一步了解volatile, 请点击此处

The primary difference between volatile and synchronized is that volatile only guarantees visibility whereas synchronized guarantees both visibility and locking. 易失性和同步性之间的主要区别在于,易失性仅保证可见性,而同步化则保证可见性和锁定。

If there are multiple read threads and one write thread then volatile usage can ensure that changes by the write thread to the volatile variable are visible to other threads immediately. 如果有多个读取线程和一个写入线程,那么volatile用法可以确保写入线程对volatile变量所做的更改立即对其他线程可见。 But you see in this case locking isn't a problem because you only have 1 writing thread. 但是在这种情况下,您会看到锁定不是问题,因为您只有1个写入线程。

There are certain rules of thumb for a volatile: 对于易失性有一些经验法则:

  1. Don't use volatile when its value depends on its previous value 当其值取决于其先前值时不要使用volatile
  2. Don't use volatile when it participates in interactions with other invariants 当它参与与其他不变量的交互时,请不要使用volatile
  3. Don't use volatile when there are multiple write threads that update value of volatile variable. 当有多个写线程更新volatile变量的值时,请不要使用volatile。

In general, use of volatile should be limited to only those cases where it's relatively easy to reason about its state such as in the case of status flags. 通常,仅在那些相对容易推断其状态的情况下(例如,状态标志),才应使用volatile。

In all other cases where you have shared mutable state always use synchronized wherever shared mutable state is being touched unless declared final and modified only in the constructor without unsafe publication. 在所有其他具有共享可变状态的情况下,无论何时触摸共享可变状态,都必须始终使用同步,除非声明为final且仅在构造函数中进行了修改且没有不安全发布。 Volatile is a replacement for synchronized only in special cases as described in my 3 points. 正如我的3点中所述,Volatile仅在特殊情况下才可以同步。

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

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