如果我们正在同步读取,我们是否需要同步写入?

时间:2017-02-10 15:52:21

标签: java multithreading heap-memory synchronized-block

我对同步块几乎没有疑问。 在我提出问题之前,我想与其他相关帖子Link for Answer to related question分享答案。我从同一个答案中引用Peter Lawrey

  1.   

    synchronized确保您拥有一致的数据视图。这意味着您将阅读最新的值,其他缓存将获得   最新价值。缓存足够聪明,可以通过一个相互交谈   特殊巴士(不是JLS要求的东西,但允许)这个   总线意味着它不必触摸主内存即可获得   一致的观点。

  2.   

    如果您只使用synchronized,则不需要volatile。如果您有一个非常简单的同步操作,则Volatile非常有用   会有点矫枉过正。

  3. 参考上文,我有以下三个问题:

    Q1。假设在多线程应用程序中,只有一个对象或一个原始实例字段只在同步块中读取(写入可能在某些其他方法中没有同步发生)。同步块也是在某个其他对象上定义的。 声明它是不稳定的(即使它只在同步块中读取)是否有意义

    Q2。 我理解已经完成同步的对象状态的值是一致的。我不确定在Synchronized块的一侧读取其他对象和原始字段的状态。 假设在没有获得锁定的情况下进行了更改,但是通过获取锁定来完成读取。 Synchronized块中所有原始字段的所有对象和值的状态是否始终具有一致的视图。

    Q3。 [更新] 无论我们锁定什么,是否会在主内存中读取同步块中读取的所有字段? [CKing回答]

    我已为上述问题准备了参考代码。

    public class Test {
      private SomeClass someObj;
      private boolean isSomeFlag;
      private Object lock = new Object();
      public SomeClass getObject() {
            return someObj;
      }
      public void setObject(SomeClass someObj) {
            this.someObj = someObj;
      }
      public void executeSomeProcess(){
            //some process...
      }
      // synchronized block is on a private someObj lock.
      // inside the lock method does the value of isSomeFlag and state of someObj remain consistent?
    
      public void someMethod(){
            synchronized (lock) {
                  while(isSomeFlag){
                        executeSomeProcess();
                  }
                  if(someObj.isLogicToBePerformed()){
                        someObj.performSomeLogic();
                  }
            }
      }
      // this is method without synchronization.
      public void setSomeFlag(boolean isSomeFlag) {
            this.isSomeFlag = isSomeFlag;
      }
    }
    

3 个答案:

答案 0 :(得分:2)

您需要了解的第一件事是,链接答案中讨论的场景与您正在谈论的场景之间存在细微差别。您谈到在没有同步的情况下修改值,而在链接的答案中的同步上下文中修改所有值。考虑到这种理解,让我们解决您的问题:

  

Q1。假设在多线程应用程序中,只有一个对象或一个原始实例字段只在同步块中读取(写入可能在某些其他方法中没有同步发生)。同步块也是在某个其他对象上定义的。声明它是不稳定的(即使它只在同步块中读取)是否有意义?

是的,将字段声明为volatile是有意义的。由于写入不在synchronized上下文中发生,因此无法保证写入线程将新更新的值刷新到主存储器。由于这个原因,阅读线程可能仍会看到不一致的值。

  

假设在没有获得锁定的情况下进行了更改,但是通过获取锁定来完成读取。 Synchronized块中所有原始字段的所有对象和值的状态是否始终具有一致的视图。 ?

答案仍然是否定的。推理与上述相同。

底线:修改同步上下文之外的值不会确保将这些值刷新到主内存。 (因为读者线程可能在写入线程之前进入同步块)在synchronized上下文中读取这些值的线程仍然可能最终读取较旧的值,即使它们从主存储器中获取这些值。

请注意,这个问题涉及原语,因此理解Java为32位基元(除了long和double之外的所有基元)提供超薄空中安全也很重要,这意味着您可以放心,您至少会看到有效值(如果不一致)。

答案 1 :(得分:1)

所有synchronized都会捕获与其同步的对象的锁定。如果已经捕获了锁,它将等待其释放。它绝不断言该对象的内部字段不会发生变化。为此,有volatile

答案 2 :(得分:1)

在对象监视器A上进行同步时,可以保证在同一监视器A上同步的另一个线程将看到第一个线程所做的任何更改任何对象。这是synchronized提供的可见性保证,仅此而已。

volatile变量保证线程之间的可见性(仅适用于变量,易失性HashMap并不意味着地图的内容可见),而不管任何同步的块。