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

问题描述:

我对同步块有一些怀疑。 在我的问题之前,我想分享来自其他相关职位Link for Answer to related question的答案。我从同一个答案引用Peter Lawrey如果我们正在同步读取,我们是否需要同步写入?

  1. 同步,确保您拥有的数据的一致视图。这意味着您将读取最新值,而其他缓存将获得最新值 。高速缓存足够聪明,可以通过 特殊总线(不是JLS所要求的但允许)相互通信。总线意味着它不必触碰主存以获得一致的视图。

  2. 如果您只使用同步,您将不需要易失性。如果你有一个非常简单的操作,同步的 会过度挥发,挥发性是有用的。

在参照上述我有以下三个问题:

Q1。假设在一个多线程应用程序中,只有一个对象或一个原始实例字段只能在一个同步块中读取(写入操作可能会发生在没有同步的其他方法中)。同步块也在其他对象上定义。 是否声明它是不稳定的(即使它只在同步块内读取)有意义? Q2302。 我了解同步已完成的对象的状态值是一致的。我不确定其他对象和原始字段在Synchronized块的读取状态。 假设在没有获得锁的情况下进行更改,但通过获取锁来完成读取。 Alwaysized块中的所有对象和所有原始字段的值的状态都始终具有一致的视图。

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

我想对此很好,但那是其中一个最诚实的工作方式。如果你在谈论JMM时想到的是“从内存中获取”的东西,那么你还没有明白它,应该远离并发。但是没有给出的代码是不正确的。 – Voo

+0

使'isSomeFlag'挥发性将解决此问题 – ControlAltDel

+0

@Voo我编辑了我的问题,抱歉使用单词记忆,我的意思是一致的.. –

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

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

是的,它宣布字段为volatile是有意义的。由于写入不在​​上下文中,因此不能保证写入线程将新刷新的值刷新到主内存。阅读线程可能仍会因此而看到不一致的值。

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

答案仍然没有。推理与上述相同。

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


注意,关于原语这个问题的谈判,因此同样重要的是要明白,Java提供外的薄空中安全 32位原语(除了长和双所有原始数据),这意味着您可以放心,您至少会看到有效的价值(如果不一致)。

+0

好吧,但链接的答案呢,它说:1.同步确保你有一个一致的数据视图。这意味着您将读取最新值,其他缓存将获得最新值。和2.如果你只使用synchronized,你不需要volatile。如果你有一个非常简单的操作,同步会过度,挥发性是有用的。他们是不是不正确或完整? –

+1

关联答案中讨论的场景与您正在谈论的场景之间存在细微的差异。你谈到修改一个没有同步的值,而所有的值都是在链接的答案的同步上下文中修改的。在同步上下文之外修改值不会确保它们被刷新到主内存。 – CKing

+0

okk ..有帮助 –

全部​​确实是捕获它的同步对象的锁。如果锁已被捕获,它将等待其释放。它并不以任何方式断言该对象的内部字段不会改变。为此,有volatile

+0

感谢您的回复,我错误地使用主内存,我已编辑的问题.. –

当你的对象监视器A上同步,可以保证在同一个显示器A在另一个线程同步之后会看到第一线作出任何对象任何变化。这就是​​所提供的可视性保证,仅此而已。

A volatile不管任何同步块,变量保证可见性(仅用于变量,易失性HashMap并不意味着映射的内容将可见)。

+0

感谢您的答案。如果假设同步已经在'ObjectA'的监视器上完成,并且我们读取同步块内部的'ObjectB'状态(由某个线程修改其他方法),它是否一致? –

+1

除非在'synchronized(ObjectA)'块中对'ObjectB'进行了修改。相同的显示器 - >更改可见。不同的显示器 - >不能保证。 – Kayaman

+0

很好..其实这是非常接近我想知道的东西。只是为了让我自己清楚一个问题:如果在同一台显示器上进行更改,则所有字段(而不仅仅是同步完成的对象)的更改都是可见的? –