为什么我需要在volatile上使用synchronized来实现多个线程?

时间:2017-03-27 20:35:42

标签: java multithreading

有些人说如果多个线程正在读/写,那么你需要使用synchronized,如果一个线程正在读/写,而另一个线程只读,那么你必须使用volatile。我不能解决这种情况。

基本上,在写操作完成后,所有读者(特别是其他线程)都可以看到volatile字段的值。

然后如果我将变量定义为volatile,则首先threadA将读取其值,threadA将更新其值并将其写入内存。此变量将对threadB可见。那为什么我需要同步块?

3 个答案:

答案 0 :(得分:2)

  

有些人说如果多个线程正在读/写,那么你需要使用synchronized,如果一个线程正在读/写,而另一个线程只读,那么你必须使用volatile。我没有区分这种情况。

这确实没有硬性规定。选择是否使用synchronizedvolatile更多地与 对象的更新有关,而不是有多少读者或作者。

例如,您可以使用AtomicLong创建包含volatile long的多个读者和作者。

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

在某些情况下,即使只有一个读写器,您也需要synchronized块。

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

在上面的示例中,由于我们正在进行多次调用以更新status对象,因此我们可能需要确保其他线程在更新num-transactions时不会看到它而不是总金额。是的,AtomicReference处理其中一些案件,但不是全部。

要清楚,标记字段volatile可确保内存同步。当您读取volatile字段时,您会跨越读取内存屏障,当您编写它时,会跨越写入内存屏障。 synchronized块在开始时具有读取内存屏障,并且块末尾的写入屏障具有互斥锁定,以确保只有一个线程可以立即进入块。 / p>

有时你只需要内存屏障来实现线程之间正确的数据共享,有时你需要锁定。

答案 1 :(得分:0)

正如评论所示,您可能会进一步阅读。但是为了给你一个想法,你可以看看这个stackoverflow question并想一想下面的场景:

您有几个需要处于正确状态的变量。但是,尽管你使它们都变得易变,但你需要时间来通过执行某些代码来更新它们。

这个代码几乎可以由不同的线程同时执行。第一个变量可能是“OK”并且以某种方式同步,但是其他一些变量可能依赖于第一个并且还不正确。因此,在这种情况下你需要一个同步块。

添加一个帖子以进一步阅读有关易变look here

的信息

答案 2 :(得分:0)

volatile和synchronized之间的主要区别是volatile只保证可见性,而synchronized保证可见性和锁定。

如果有多个读取线程和一个写入线程,则volatile的使用可以确保写入线程对volatile变量的更改立即对其他线程可见。但是你看,在这种情况下,锁定不是问题,因为你只有1个写线程。

对于不稳定因素有一些经验法则:

  1. 当其值取决于其之前的值
  2. 时,请勿使用volatile
  3. 当参与与其他不变量的交互时不要使用volatile
  4. 当有多个写入线程更新volatile变量的值时,不要使用volatile。
  5. 一般情况下,volatile的使用应仅限于那些相对容易推理其状态的情况,例如状态标志。

    在所有其他共享可变状态的情况下,除了声明final和仅在构造函数中修改而没有不安全的发布时,总是使用synchronized来触及共享可变状态。 Volatile是仅在特殊情况下同步的替代品,如我的3点所述。