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

Do we need to synchronize writes if we are synchronizing reads?

我对同步块几乎没有怀疑。 在我提出问题之前,我想分享来自同一答案的另一个相关 post Link for Answer to related question. I quote Peter Lawrey 的答案。

  1. synchronized ensures you have a consistent view of the data. This means you will read the latest value and other caches will get the latest value. Caches are smart enough to talk to each other via a special bus (not something required by the JLS, but allowed) This bus means that it doesn't have to touch main memory to get a consistent view.

  2. If you only use synchronized, you wouldn't need volatile. Volatile is useful if you have a very simple operation for which synchronized would be overkill.

关于以上,我有以下三个问题:

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

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

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;
  }
}

synchronized 所做的只是捕获同步对象的锁。如果锁已经被捕获,它将等待释放。它不以任何方式断言该对象的内部字段不会改变。为此,有 volatile

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

A volatile 变量保证线程之间的可见性(仅对于变量,volatile HashMap 并不意味着映射的内容可见),而不管任何同步块。

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

Q1. Suppose in a multi threaded application there is an object or a primitive instance field being only read in a synchronized block (write may be happening in some other method without synchronization). Also Synchronized block is defined upon some other Object. Does declaring it volatile (even if it is read inside Synchronized block only) makes any sense ?

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

Suppose changes are made without obtaining a lock but reading is done by obtaining a lock. Does state of all the objects and value of all primitive fields inside a Synchronized block will have consistent view always. ?

答案仍然是否定的。道理同上。

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


请注意,这个问题讨论的是原语,因此了解 Java 为 32 位原语(所有原语)提供 Out-of-thin-air 安全性 也很重要long 和 double 除外),这意味着您可以放心,您至少会看到一个有效值(如果不一致)。