我正在阅读这篇博文。
而作者正在谈论hashCode()
在String
多线程环境中闯入。
有了:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
变成:
public int hashCode() {
if (hash == 0) {
int off = offset;
char val[] = value;
int len = count;
int h = 0;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return hash;
}
作者说,我引用:
“我在这里所做的是添加一个额外的读取:在 return 之前第二次读取哈希。听起来很奇怪,而且不太可能发生,第一次读取可以返回正确计算的哈希值, “
所以进一步浏览评论,有人说它可以重新排序
int h = hash;
if (hash == 0) {
...
}
return h;
这怎么可能?我认为重新排序只涉及上下移动程序语句。它遵循什么规则?我在 Google 上搜索过,阅读了 JSR133 常见问题解答,查看了 Java 并发实践一书,但我似乎找不到一个可以帮助我理解特别是重新排序的地方。如果有人能指出我正确的方向,我将不胜感激。
感谢 Louis 阐明了“重新排序”的含义,我并没有考虑“字节码”
但是,我仍然不明白为什么允许将 2nd Read 移到前面,这是我将其转换为某种“字节码”格式的天真尝试。
为简化起见,用于计算哈希码的操作表示为calchash()
. 因此,我将程序表示为:
if (hash == 0) {
h = calchash();
hash = h;
}
return hash;
我尝试以“字节码”形式表达它:
R1,R2,R3 are in the operands stack, or the registers
h is in the array of local variables
按节目顺序:
if (hash == 0) { ---------- R1 = read hash from memory (1st read)
---------- Compare (R1 == 0)
h = calchash(); ---------- R2 = calchash()
---------- h = R2 (Storing the R2 to local variable h)
hash = h; ---------- Hash = h (write to hash)
}
return hash ---------- R3 = read hash from memory again(2nd read)
---------- return R3
重新排序的转换(我的版本基于评论):
---------- R3 = read hash from memory (2nd read) *moved*
if (hash == 0) { ---------- R1 = read hash from memory (1st read)
---------- Compare (R1 == 0)
h = calchash(); ---------- R2 = calchash()
---------- h = R2 (Storing the R2 to local variable h)
hash = h; ---------- hash = h (write to hash)
}
return hash ---------- return R3
再次查看评论,我发现作者回答了这个问题:
重新排序的转换(来自博客)
r1 = hash;
if (hash == 0) {
r1 = hash = // calculate hash
}
return r1;
这种情况实际上适用于单线程,但多线程可能会失败。
似乎 JVM 正在根据
h = hash and it simplifies the use of R1, R2, R3 to single R1
因此,JVM 所做的不仅仅是重新排序指令,它似乎还减少了正在使用的寄存器数量。