【问题标题】:Why this code finishes if getter is marked as synchronized?如果 getter 被标记为同步,为什么这段代码会完成?
【发布时间】:2018-09-08 19:07:11
【问题描述】:

为什么当方法get() 被标记为同步时,尽管字段value 不是易失性,但这段代码成功完成?如果没有同步,它会在我的机器上无限期地运行(如预期的那样)。

public class MtApp {

    private int value;

    /*synchronized*/ int get() {
        return value;
    }

    void set(int value) {
        this.value = value;
    }

    public static void main(String[] args) throws Exception {
        new MtApp().run();
    }

    private void run() throws Exception {
        Runnable r = () -> {
            while (get() == 0) ;
        };
        Thread thread = new Thread(r);
        thread.start();
        Thread.sleep(10);
        set(5);
        thread.join();
    }
}

【问题讨论】:

  • 如果没有正确同步,程序结果是不确定的。程序可以正确运行和完成,也可以不正确,这完全取决于 JVM 实现的突发奇想(并且该实现可能随时更改)。

标签: java multithreading synchronized non-volatile


【解决方案1】:

同步强制this.value = value发生在get()之前。

确保更新值的可见性。

没有同步,就没有这样的保证。它可能有效,也可能无效。

【讨论】:

    【解决方案2】:

    @Andy Turner 部分正确。

    get() 方法上添加synchronized 会影响内存可见性要求,并导致(JIT)编译器生成不同的代码。

    但是,严格来说,需要有一个 happens before 关系连接set(...) 调用和get() 调用。这意味着set 方法应该是synchronizedget(如果你打算这样做的话!)。

    简而言之,您观察到的代码版本不能保证在所有平台和所有情况下都能正常工作。事实上,你很幸运!


    从字里行间看,您似乎正在尝试通过实验来弄清楚 Java 的内存模型是如何工作的。 这不是一个好主意。问题是您正试图在没有足够“输入参数”的情况下对一个非常复杂的黑盒进行逆向工程1让您改变以涵盖黑盒行为的所有潜在方面。

    因此,“通过实验学习”的方法可能会让您产生不完整或错误的理解。

    如果您想要一个完整而准确的理解,您应该开始阅读一本好教科书中的 Java 内存模型...或 JLS 本身。无论如何,使用实验来尝试确认您的理解,但您确实需要注意 JMM 指定(保证)如果您做正确的事情会发生什么。如果您做错了事,您的代码可能仍然有效...取决于各种因素。因此,通常难以通过实验确认某种特定的做事方式是正确还是不正确2

    1 - 您需要的某些参数实际上并不存在。例如,允许您在 N > 12 的情况下运行 Java N 的一种,或者允许您在您无权访问的硬件上运行的一种...或尚不存在的硬件。

    2 - 如您的示例所示。即使代码错误,您也得到了“正确”的答案。

    【讨论】:

    • 这是一个非常详细且正确的解释,应该标记为正确答案。
    • 感谢您的详尽回答@Stephen!我确实知道这段代码有问题(无论是否在 getter 上同步),我绝对不会在生产中使用类似的东西。
    【解决方案3】:

    对于初学者,value 必须是 volatilegetset 都需要是 synchronized 才能正确。

    JLS 17.4.5:

    监视器上的解锁发生在该监视器上的每个后续锁定之前。

    value 可以在锁被释放之前设置为 5,这会将其置于 happens-before 边缘之前,并使其在下次锁被释放时可用获得。

    应该注意,这样的保证是脆弱的,并且取决于线程调度程序,可能根本不存在。在同步模型较弱的平台上,您可能不会看到与此处相同的效果。


    另请参阅: Loop doesn't see changed value without a print statement

    Strange behavior of a Java thread associated with System.out

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-21
      • 2013-12-06
      • 1970-01-01
      • 1970-01-01
      • 2011-02-20
      • 2018-10-28
      • 2013-04-27
      • 2012-06-06
      相关资源
      最近更新 更多