当前位置: 首页 > 知识库问答 >
问题:

如果 getter 被标记为已同步,为什么此代码会完成?

冯元徽
2023-03-14

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

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

共有3个答案

廖琨
2023-03-14

对于初学者来说,value需要易失性set需要同步

JLS:

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

在释放锁之前,可以将值设置为 5,这会将其放在发生之前的边缘之前,并在下次获取锁时使其可用。

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

另请参阅:循环在没有打印语句的情况下看不到更改的值

与System.out关联的Java线程的奇怪行为

仲孙疏珂
2023-03-14

@Andy特纳部分正确。

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

但是,严格来说,在连接set(…)调用和get()调用之前需要发生关系。这意味着set方法应该是同步的以及get(如果您要这样做!)。

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

从字里行间看,您似乎在试图通过实验了解Java的内存模型是如何工作的。这不是一个好主意。问题是,您正在尝试对一个极其复杂的黑盒进行反向工程,但没有足够的“输入参数”1,使您可以进行更改以涵盖黑盒行为的所有潜在方面。

因此,“通过实验学习”的方法很容易让你产生不完整或错误的理解。

如果你想完整而准确地理解,你应该从阅读一本好的教科书中的Java内存模型开始……或者JLS本身。无论如何,使用实验来尝试确认您的理解,但您需要知道,JMM只指定(保证)在您做正确的事情时会发生什么。如果你做了错误的事情,你的代码可能无论如何都能工作……这取决于各种因素。因此,通常很难通过实验来确认一种特定的做事方式是正确的还是不正确的2

< sup>1 -您需要的一些参数实际上并不存在。比如,允许你运行Java N for N的那个

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

邵凯定
2023-03-14

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

它确保了更新值的可见性。

没有同步,就没有这样的保证。它可能工作,也可能不工作。

 类似资料:
  • 问题内容: 我编写了以下程序: 由于通道事件列表是一个缓冲通道,我想我应该获得100倍的输出“嘿!”,但是它只显示一次。我的错误在哪里? 问题答案: 更新(Go 1.2版或更高版本) 从Go 1.2开始,调度程序基于 抢先式多任务处理 原则。这意味着原始问题(以及下面提供的解决方案)中的问题不再相关。 从Go 1.2发行说明中 调度程序中的抢占 在以前的版本中,永远循环的goroutine可能会使

  • 问题内容: 我已经找到了这段代码,无法找出解决方法。在运行代码时,为什么不提示用户输入而不是Java确定没有输入?错误跟踪如下。 这是错误- 问题答案: 简单的答案是当您关闭扫描仪时- 基础输入流也会关闭:http : //docs.oracle.com/javase/7/docs/api/java/util/Scanner.html#close() 要修复此问题,请在主菜单中创建一次:

  • 问题内容: var x int done := false go func() { x = f(…); done = true } while done == false { } 这是Go代码。我的恶魔告诉我,这是UB代码。为什么? 问题答案: Go Memory Model不保证该程序将始终遵守在goroutine中写入x的值。go常规销毁 部分中提供了一个类似的错误程序作为示例。 在本节中,G

  • 问题内容: 在方法或类范围内,下面的行进行编译(带有警告): 在类范围中, 变量获取其默认值 ,以下给出“未定义引用”错误: 它不是第一个应该以相同的“未定义参考”错误结束吗?还是第二行应该编译?还是我缺少什么? 问题答案: tl; dr 对于 字段 ,是非法的,因为它是对的非法前向引用。您实际上可以通过编写来解决此问题,该文件可以毫无抱怨地进行编译。 对于 局部变量 ,是非法的,因为未在使用前进

  • 问题内容: 为什么这段代码不抛出?它在不使用方法的情况下修改了一段时间,这是唯一安全的删除方法。 如果将替换为,则会得到相同的结果。但是,如果我将列表更改为或只是得到了预期的异常。到底是怎么回事?我正在使用是否相关。 编辑 我找到了以下链接 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078 相关部分是 天真的解决方案是将协同修改

  • 下面的代码拆分数据,应用正则表达式,然后再次连接字符串(有一部分删除了单词之间的新行,因为我希望在单个块/行中输出段落): 输入: Lorem ipsum dolor坐在一起 Ipsum dolor sit amet,consetetur eirmod tempor invidunt ut laboure 代码: 我认为输入将是: 但不是,而是: 原因可能是什么?