今天我实验室的一个敏感操作完全出了问题。一台电子显微镜上的一个致动器越过了它的边界,在一系列事件之后,我损失了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溢出时表现良好,所以我认为这段代码没有问题。然而,尽管输出表明程序没有达到退出条件,但它却达到了退出条件(既达到了又没有达到?)。为什么?
我注意到在某些环境中不会发生这种情况。我在64位Linux上使用OpenJDK6。
由于currentpos
是在线程外部更改的,因此应将其标记为volatile
:
static volatile Point currentPos = new Point(1,2);
如果没有volatile,html" target="_blank">线程就不能保证读取主线程中对currentPos的更新。因此会继续为currentPos编写新的值,但出于性能原因,thread会继续使用以前的缓存版本。因为只有一个线程修改currentPos,所以您可以在没有锁的情况下离开,这将提高性能。
如果在线程中只读取一次值以用于比较和随后的显示,结果看起来会有很大的不同。当我执行以下操作时,x
总是显示为1
,y
在0
和某个大整数之间变化。我认为,在没有volatile
关键字的情况下,它的行为在某种程度上是未定义的,可能是代码的JIT编译导致了它的这种行为。此外,如果注释掉空的synchronized(this){}
块,那么代码也可以正常工作,我怀疑这是因为锁定引起了足够的延迟,currentpos
及其字段被重新读取,而不是从缓存中使用。
int x = p.x + 1;
int y = p.y;
if (x != y) {
System.out.println(x+" "+y);
System.exit(1);
}
很明显,在读取之前,写currentPos是不会发生的,但我不认为这会是问题所在。
currentpos=new Point(currentpos.x+1,currentpos.y+1);
执行了一些操作,包括将默认值写入x
和y
(0),然后将它们的初始值写入构造函数。由于您的对象没有安全地发布,编译器/JVM可以自由地重新排序这4个写操作。
因此,从读取线程的角度来看,使用新值读取x
而使用默认值0读取y
是合法的执行。当您到达println
语句(顺便说一下,该语句是同步的,因此会影响读取操作)时,变量已经有了它们的初始值,程序将打印期望值。
将currentpos
标记为volatile
将确保安全发布,因为您的对象实际上是不可变的--如果在您的实际用例中,对象在构造后发生了变化,volatile
的保证是不够的,您可能会再次看到不一致的对象。
或者,您可以使点
不可变,这也将确保安全发布,即使不使用volatile
。要实现不变性,只需将x
和y
标记为final。
作为附带说明,正如前面提到的,synchronized(this){}
可以被JVM视为no-op(我知道您包含它是为了再现该行为)。
问题内容: 今天,我实验室中的一个敏感操作完全出错。电子显微镜上的执行器越过边界,经过一连串的事件,我损失了1200万美元的设备。我将有故障的模块中的超过40K行缩小为: 我得到的一些输出示例: 由于这里没有任何浮点算法,而且我们都知道有符号整数在Java中的溢出情况下表现良好,因此我认为这段代码没有错。但是,尽管输出表明程序未达到退出条件,但程序仍达到了退出条件(是否达到 和 未达到?)。为什么
我有一个简单的功能,我想测试,但明显的结果是没有发生。。。 我的函数是如何工作的(实际上它确实工作,只是没有正确测试) 我将字符串传递到函数中 当我运行所示的测试时,我收到错误: 预期默认值等于hare失败 我的组件 我的测试
你好Stack overflow社区, 我的提示是:**“您将进行错误检查,以确保输入的小时在[0,23]范围内。继续询问用户,直到用户输入有效范围内的时间。 当我的程序有正确的整数输入(任何整数输入)时,它将正常工作。然而,如果我输入一个非整数,那么所有的东西都卡在一个无限循环中。 我做错了什么?
我在中使用带有4.1版本模拟器的android最新版本sdk。一切都很好。但是在我的中,对于任何应用程序的每次运行,我都会得到以下语句。 即使在Hello world应用程序中,我也得到相同的logcat输出。我没有在我的应用程序中使用多线程。有人能告诉我为什么在我的logcat中得到这些日志。 这是我的密码 在我的异步任务中,我从服务器获取JSONArray,解析它并列出。
自从在JDK1.0中引入Object#Finalize()之后,Java又推出了9个版本。 然而,尽管它有明显的缺点,但它仍然可用,即使在JDK10中也是如此。 这就引出了一个问题:这种方法是否有任何已知的正确用法?
大家好,我在项目中遇到了一个大问题。我发出OkHttp请求,从服务器收到响应,一切都很完美。问题是为什么我不能显示数据,我在xml和listview中创建了一个模型项(这是必需的视图),当我访问该页面时,该页面为空。 有人能帮我解决这个问题吗? 页面代码如下: