繁体   English   中英

Java的“字符串”哈希码函数是否是线程安全的,如果它的缓存设置器不使用锁?

[英]Is Java's “String” hashcode function thread-safe if its cache setter does not use locks?

这是来自Java的String hashCode函数的代码

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

如您所见,它检查散列(“private int hash”)== 0并以其他方式设置它。 构造函数并不总是设置此值(以及为什么还要检查当然)。

因此,虽然在实际应用中很难再现,但看起来这个哈希上的竞争条件似乎正确吗?

我的意思是,一旦你把它放在一个hashmap中,它就会安全,除非你先把它发送到另一个线程。 但是如果字符串在两个线程上并同时添加到散列映射中,则hashMap函数可以采用部分写入的“散列”值并返回它。

从理论上讲,可以生成一些代码,这些代码会导致多个同时线程读取0值哈希并进入计算部分。 这将是“浪费”,但是安全,因为函数对不可变字符进行操作,并且每个实例将计算完全相同的散列值。

总结注释部分的结果 - 整数的读取和写入在Java VM中以原子方式完成。

可以在Oracle网站上的“Atomic Access”下找到相关规范。

https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

我不使用Java,但这似乎不是一个“竞争条件”。 我会说它是“懒惰计算哈希,然后缓存值”。

因此,如果没有人会调用hashCode方法,那么在构造函数中计算哈希是没有用的。 但是在第一个调用hashCode之后,那么该值将被计算,永远不会改变,因此您可以将其缓存以供进一步调用。

后期编辑:

现在我明白你的意思了。 这是局部变量的目的。 而不使用“线程安全”机制是一个“性能决定”,因为创建一个锁,使用它,释放它需要一个成本,而你正在谈论的情况(2个线程同时调用hashCode)是一个在现实生活场景中很难达到,结果将是相同的哈希值。

根据Java内存模型,对hashCode读取和写入未正确同步,但它仍然是安全的。

如果多个线程写入hashCode那么由于String对象的不变性,隐含的是计算产生相同的结果。 假设此结果为x则保证任何线程都观察到0x因为int在所有VM上都是原子的。 如果一个线程观察到0 ,它只是重新计算保证产生x的哈希码,因此只有在另一个线程同时或在其线程本地缓存中应用该操作时才重置该值。

从这个意义上讲,结果是确定性的。 同时,不需要同步线程来共享此实例。 假设在所有线程使用的应用程序中都有一些关键的"foo" 由于Java的字符串重复数据删除,这个字符串常量将在所有线程之间共享,这些线程必须同步才能省去重新计算哈希码的麻烦。 然而,计算哈希码是非常便宜的操作,而同步非常昂贵。 由于给出了正确性,这种优化是有道理的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM