为什么这个 Java 程序会终止,尽管它显然不应该(也没有)终止?

2024-04-10

今天我实验室的一项敏感操作完全出错了。电子显微镜上的执行器超出了其边界,在发生一系列事件后,我损失了 1200 万美元的设备。我已将故障模块中超过 40K 行的范围缩小为:

import java.util.*;

class A {
    static Point currentPos = new Point(1, 2);
    static class Point {
        int x;
        int y;
        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    public static void main(String[] args) {
        new Thread() {
            void f(Point p) {
                synchronized(this) {}
                if (p.x + 1 != p.y) {
                    System.out.println(p.x + " " + p.y);
                    System.exit(1);
                }
            }
            @Override
            public void run() {
                while (currentPos == null);
                while (true)
                    f(currentPos);
            }
        }.start();
        while (true)
            currentPos = new Point(currentPos.x + 1, currentPos.y + 1);
    }
}

我得到的一些输出示例:

$ java A
145281 145282
$ java A
141373 141374
$ java A
49251 49252
$ java A
47007 47008
$ java A
47427 47428
$ java A
154800 154801
$ java A
34822 34823
$ java A
127271 127272
$ java A
63650 63651

由于这里没有任何浮点运算,而且我们都知道有符号整数在 Java 中溢出时表现良好,所以我认为这段代码没有任何问题。然而,尽管输出表明程序没有达到退出条件,但它达到了退出条件(它都达到了and还没到?)。为什么?


我注意到这在某些环境中不会发生。我上线了OpenJDK http://en.wikipedia.org/wiki/OpenJDK6 在 64 位 Linux 上。


显然,对 currentPos 的写入不会发生在读取之前,但我不明白这怎么会成为问题。

currentPos = new Point(currentPos.x+1, currentPos.y+1);做一些事情,包括将默认值写入x and y(0) 然后将它们的初始值写入构造函数中。由于您的对象未安全发布,因此编译器/JVM 可以自由地重新排序这 4 个写入操作。

所以从读取线程的角度来看,读取是合法执行的x具有新的价值,但是y例如,其默认值为 0。当您到达println语句(顺便说一下,它是同步的,因此会影响读取操作),变量有其初始值,程序会打印预期值。

Marking currentPos as volatile将确保安全发布,因为您的对象实际上是不可变的 - 如果在您的实际用例中该对象在构造后发生了变化,volatile保证还不够,您可能会再次看到不一致的对象。

或者,您可以使Point不可变,即使不使用,也将确保安全发布volatile。要实现不变性,您只需标记x and y final.

作为旁注,正如已经提到的,synchronized(this) {}可以被 JVM 视为无操作(我知道您将其包含在内是为了重现该行为)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这个 Java 程序会终止,尽管它显然不应该(也没有)终止? 的相关文章

随机推荐

  • 测试组件,这取决于路由参数

    我有一个关于在 angular2 中测试路由组件的问题 这是一个简单的组件 它依赖于带有参数的路由 foo 属性foo组件中的值将被设置为参数的值 import Component OnInit from angular core impo
  • TFS 构建定义 - 您可以将其添加到源代码管理中吗?

    我想将我的构建定义添加到 TFS 源代码管理中 有办法做到这一点吗 不完全是 不 如果您尝试跟踪构建定义的更改 您可以使用最新版本中的几个新命令Team Foundation 电动工具 http visualstudiogallery ms
  • 如何将 JTextField 限制为 x 个字符

    我必须限制 JTextField 中的字符数 我使用以下代码来执行此操作 但问题是我使用虚拟键盘将数据输入到 JTextField 所以偏移量始终设置为0 当我输入超过指定数量的字符时 它会重置该字段并从头开始执行 例如 如果我的限制是 3
  • 使用 ng-file-upload 和 Rails Carrierwave gem 上传多个文件

    我正在尝试结合ng 文件上传 and 载波上传多个文件 但服务器端的控制器只接收一个文件 所选文件的最后一项 客户端 参考 https github com danialfarid ng file upload usage html
  • 如何在 ejs 文件、nodeJS 应用程序中包含 JS 脚本?

    我正在做 NodeJS 教程开放课堂 https openclassrooms com courses des applications ultra rapides avec node js tp la todo list 我使用模板引擎e
  • 如何使用函数指针数组?

    我应该如何在 C 中使用函数指针数组 我怎样才能初始化它们 你有一个很好的例子这里 函数指针数组 http www java2s com Code C Function Arrayoffunctionpointer htm 与语法详细 ht
  • Webpack url 和文件加载器不适用于 Angular 2 所需的组件样式

    我正在使用 Angular 2 sass 和 webpack 构建一个应用程序 但我在每个组件内所需的 sass 文件上的 url 上遇到了麻烦 使用 require 它不会将这些文件复制到资产文件夹中 也不会将 url 修改为构建的 cs
  • 如何使用宏引用数据文件?

    我有各种 Stata 数据文件 它们位于不同的文件夹中 我也有一个单do使用这些文件的文件 一次一个 有没有办法使用宏来引用我的特定数据集do file 例如 local datafile C filepath mydata dta 我们的
  • 如何缩小java堆空间? [复制]

    这个问题在这里已经有答案了 我有一个 Java 控制台应用程序 它使用 DOM 处理大型 xml 文件 基本上 它根据从数据库获取的数据创建 xml 文件 现在 正如您猜测的那样 它使用了大量内存 但令我惊讶的是 它与错误代码无关 而是与
  • Jetpack Compose 失败,并显示 NoClassDefFoundError: 无法解析:Landroidx/compose/runtime/MutableStateKt;

    我构建了一个应用程序来使用 Jetpack compose 在 alpha 11 之前一切都工作正常 但由于我尝试将其更新到 alpha 12 甚至 beta 1 该应用程序在运行时失败并出现错误NoClassDefFoundError F
  • 如何设置占位符文本的颜色和字体样式

    我想将颜色设置为占位符 将字体样式更改为粗体 并增加大小 我怎样才能实现这个目标 我应该为占位符提供样式 还是有其他方法可以实现此目的 我想设置颜色并更改字体样式以在所有浏览器中工作 以在下面的结果中选择大小
  • 为什么点击事件总是不触发?

    如果您重新审视这个问题 我已将所有更新移至底部 因此它实际上作为一个问题读起来更好 问题 我在使用处理浏览器事件时遇到了一些奇怪的问题D3 不幸的是 这位于一个相当大的应用程序中 并且因为我完全不知道原因是什么 所以我正在努力寻找一个小的可
  • 修改栈上的返回地址

    我研究了缓冲区溢出漏洞的基础知识 并尝试了解堆栈是如何工作的 为此 我想编写一个简单的程序 将返回地址的地址更改为某个值 有人可以帮助我计算基指针的大小以获得第一个参数的偏移量吗 void foo void char ret char pt
  • C# 帮助:在 C# 中对对象列表进行排序 [重复]

    这个问题在这里已经有答案了 可能的重复 使用预定义的排序值列表对对象进行排序 https stackoverflow com questions 652337 sort objects using predefined list of so
  • 根据求解器的决定执行 get-model 或 unsat-core

    我想知道 SMT LIB 2 0 脚本中是否有可能访问求解器的最后一个可满足性决策 sat unsat 例如 以下代码 set option produce unsat cores true set option produce model
  • 选择树形布局中子节点的所有路径和父节点

    我正在跟进this http bl ocks org d3noob 8375092学习 d3 js 树布局的教程 我正在研究它 单击子节点时 我尝试选择所有祖先节点以及连接它们的路径 我已经更改了默认值click教程中的函数看起来像这样 f
  • jQuery - jQGrid - 展开、折叠网格行上的子网格单击

    Here https stackoverflow com questions 3345401有一个关于当我们单击一行时如何扩展子网格的答案 onSelectRow function rowId jqgrid id expandSubGrid
  • 我应该检查什么:cpu 时间还是 wall time?

    我有两种算法来完成相同的任务 要检查它们的性能 我应该检查什么 cpu 时间还是 wall time 我认为这是CPU时间 对吗 我正在对我的代码进行并行处理 要检查我的并行性能 我应该检查什么 cpu 时间还是 wall time 我想现
  • 使用.Net检测灰度图像

    我正在将文档扫描为 JPG 图像 扫描仪必须将所有页面扫描为彩色或将所有页面扫描为黑白 由于我的许多页面都是彩色的 因此我必须将所有页面扫描为彩色 扫描完成后 我想使用 Net 检查图像并尝试检测哪些图像是黑白的 以便我可以将这些图像转换为
  • 为什么这个 Java 程序会终止,尽管它显然不应该(也没有)终止?

    今天我实验室的一项敏感操作完全出错了 电子显微镜上的执行器超出了其边界 在发生一系列事件后 我损失了 1200 万美元的设备 我已将故障模块中超过 40K 行的范围缩小为 import java util class A static Po