有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么我需要在volatile上对多个线程使用synchronized?

有些人说,如果多个线程正在读/写,那么您需要使用synchronized;如果一个线程正在读/写,而另一个线程仅在读,那么您必须使用volatile。我不明白这两种情况之间的区别

基本上,一个volatile字段的值在写操作完成后对所有读卡器(特别是其他线程)都可见

然后,如果我将一个变量定义为volatile,首先threadA将读取它的值,threadA将更新它的值并将其写入内存。之后,threadB将看到该变量。那为什么我需要同步块呢


共 (3) 个答案

  1. # 1 楼答案

    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. I don't get the difference between this situations.

    这真的没有硬性规定。选择是否使用synchronizedvolatile与对象的更新方式(而不是读写器的数量)有更多关系

    例如,您可以使用AtomicLong实现多个读写器,该volatile long封装了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事务(而不是总金额)时不会看到它。是的,AtomicReference处理其中一些案例,但不是全部

    明确地说,标记字段volatile可以确保内存同步。当您读取volatile字段时,您会跨越一个读取内存屏障,而当您写入它时,您会跨越一个写入内存屏障。synchronized块的开头有一个读内存屏障,结尾有一个写屏障,具有互斥锁,以确保一次只能有一个线程进入该块

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

  2. # 2 楼答案

    volatile和synchronized之间的主要区别在于volatile只保证可见性,而synchronized同时保证可见性和锁定

    如果有多个读线程和一个写线程,那么volatile用法可以确保写线程对volatile变量的更改对其他线程立即可见。但在本例中,锁定不是问题,因为您只有一个写入线程

    对于易失性,有一些经验法则:

    1. 当volatile的值取决于它以前的值时,不要使用volatile
    2. 当volatile参与与其他不变量的交互时,不要使用volatile
    3. 当有多个写线程更新volatile变量的值时,不要使用volatile

    一般来说,volatile的使用应该仅限于那些相对容易解释其状态的情况,例如状态标志的情况

    在所有其他具有共享可变状态的情况下,除非声明为final并仅在构造函数中修改,否则在触及共享可变状态的任何位置都始终使用synchronized,而不使用不安全的发布。Volatile仅在特殊情况下替代synchronized,如我的3点所述

  3. # 3 楼答案

    正如评论所建议的,你可以做一些进一步的阅读。但是为了给你一个想法,你可以看看这个stackoverflow question并思考以下场景:

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

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

    为了进一步阅读volatilelook here,再添加一篇文章