java - ConcurrentHashMap 重新排序指令?

标签 java multithreading concurrency

我正在研究 ConcurrentHashMap 的实现,但有件事让我感到困惑。

/* Specialized implementations of map methods */

        V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry<K,V> e = getFirst(hash);
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.value;
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck
                    }
                    e = e.next;
                }
            }
            return null;
        }

    /**
     * Reads value field of an entry under lock. Called if value
     * field ever appears to be null. This is possible only if a
     * compiler happens to reorder a HashEntry initialization with
     * its table assignment, which is legal under memory model
     * but is not known to ever occur.
     */
    V readValueUnderLock(HashEntry<K,V> e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }

和 HashEntry 构造函数

/**
     * ConcurrentHashMap list entry. Note that this is never exported
     * out as a user-visible Map.Entry.
     *
     * Because the value field is volatile, not final, it is legal wrt
     * the Java Memory Model for an unsynchronized reader to see null
     * instead of initial value when read via a data race.  Although a
     * reordering leading to this is not likely to ever actually
     * occur, the Segment.readValueUnderLock method is used as a
     * backup in case a null (pre-initialized) value is ever seen in
     * an unsynchronized access method.
     */
    static final class HashEntry<K,V> {
    final K key;
            final int hash;
            volatile V value;
            final HashEntry<K,V> next;

            HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
                this.key = key;
                this.hash = hash;
                this.next = next;
                this.value = value;
            }

放置工具

tab[index] = new HashEntry<K,V>(key, hash, first, value);

我对 HashEntry 评论感到困惑,因为 JSR-133 , 一旦构造了 HashEntry,所有 final 字段将对所有其他线程可见,value 字段是易变的,所以我认为它对其他线程也可见??? .另一点,他说的重新排序是:HashEntry 对象引用可以在 tab[...] 完全构造之前分配给它(所以结果是其他线程可以看到这个条目,但 e.value 可以为 null)?

更新: 我读了this文章,很好。但是我需要关心这样的案例吗

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

thread1:

Person p=new Person("name","student");        
queue.offer(new Person());

thread2:
Person p = queue.poll();

thread2 是否有可能像 HashEntry 一样接收到一个未完成构造的 Person 对象

tab[index] = new HashEntry(key, hash, first, value); ?

最佳答案

对于那些对 Doug Lea 的回答感兴趣的人,他最近解释了 readValueUnderLock 的原因

这是对有问题的人的回应:

In the ConcurrentHashMap the get method does not require "readValueUnderLock" because a racing remove does not make the value null. The value never becomes null on the from the removing thread. this means it is possible for get to return a value for key even if the removing thread (on the same key) has progressed till the point of cloning the preceding parts of the list. This is fine so long as it is the desired effect.

But this means "readValueUnderLock" is not required for NEW memory model.

However for the OLD memory model a put may see the value null due to reordering(Rare but possible).

Is my understanding correct.

响应:

Not quite. You are right that it should never be called. However, the JLS/JMM can be read as not absolutely forbidding it from being called because of weaknesses in required ordering relationships among finals vs volatiles set in constructors (key is final, value is volatile), wrt the reads by threads using the entry objects. (In JMM-ese, ordering constraints for finals fall outside of the synchronizes-with relation.) That's the issue the doc comment (pasted below) refers to. No one has ever thought of any practical loophole that a processor/compiler might find to produce a null value read, and it may be provable that none exist (and perhaps someday a JLS/JMM revision will fill in gaps to clarify this), but Bill Pugh once suggested we put this in anyway just for the sake of being conservatively pedantically correct. In retrospect, I'm not so sure this was a good idea, since it leads people to come up with exotic theories.

都可以查看here

关于java - ConcurrentHashMap 重新排序指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5002428/

相关文章:

java - 从千兆字节文件中读取

java - 为什么JAVA_HOME没有设置但Gradle仍然可以运行?

java - wait()/notify() 无法正常工作

swift - 如果队列不为空,则跳过新任务。 swift

并发模型: Erlang vs Clojure

java - 在不使用数组或循环的情况下找到五个给定数字中第三大的最快方法?

java - Weblogic DB2 数据显示为 '?????' 问号

Android AsyncTask onPostExecute 关闭主 ui 线程

android - Android 中 SingleThreadExecutor 和 Handler 的区别(举例)

javascript - 尝试打印有警报的 URL 时如何解决并发问题?