ConcurrentHashMap重新排序指令? [英] ConcurrentHashMap reorder instruction?

查看:144
本文介绍了ConcurrentHashMap重新排序指令?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  / *专门实现映射方法* / 

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.quals(e.key)){
V v = e.value;
if(v!= null)
return v;
return readValueUnderLock(e); // recheck
}
e = e.next;
}
}
return null;
}

  / ** 
*读取锁定条目的值字段。如果值
*字段显示为null,则调用。这是可能的,只有当
*编译器碰巧重新排序HashEntry初始化与
*它的表分配,这在内存模型
*是合法的,但不知道永远不会发生。
* /
V readValueUnderLock(HashEntry< K,V> e){
lock();
try {
return e.value;
} finally {
unlock();
}
}

和HashEntry构造函数

  / ** 
* ConcurrentHashMap列表条目。注意,从不导出
* out作为用户可见的Map.Entry。
*
*因为value字段是volatile,不是final,所以它是合法的wrt
* Java内存模型为一个不同步的阅读器看到null
*而不是初始值通过数据竞赛阅读。虽然导致这种情况的
*重新排序不太可能实际发生
*,但是如果空(预初始化)值为空,则Segment.readValueUnderLock方法用作
*备份在
*中看到一个不同步的访问方法。
* /
static final class HashEntry< K,V> {
final K key;
final int hash;
volatile V value;
final HashEntry< K,V>下一个;

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

put implement

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

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



更新:
我阅读了这篇文章,很好。但是我需要关心像这样的情况

  ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue 

thread1:

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

thread2:
Person p = queue.poll();

有可能thread2接收未完成构造的Person对象,就像HashEntry在


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



解决方案

对于那些对Doug Lea题目,他最近解释了 readValueUnderLock



的原因这是为了回答有问题的人: p>


在ConcurrentHashMap中,get
方法不需要
readValueUnderLock,因为比赛
remove使值为null。
该值永远不会在删除线程的
上为null。这意味着
有可能为key返回一个
值,即使删除
线程(在同一个键上)有
进行到克隆的时候
列表的前面的部分。



但是这意味着readValueUnderLock是
,不需要新的
内存模型。



然而,对于OLD内存模型,由于
重新排序(Rare但可能),
可能会看到值为null。 p>

我的理解是否正确。


回应:


不完全。你是对的,
永远不会被调用。然而,
JLS / JMM可以读作不是绝对的
禁止它被称为
因为所需的弱点
在决赛之间的顺序关系vs
挥发性设置构造函数(关键是
final,值是volatile),wrt
通过线程使用条目
对象读取。 (在JMM-ese中,排序
限制在
之外的同步关系。)
这是文档注释
(下面粘贴)所指的问题。没有人有
曾经想过任何实际的漏洞
处理器/编译器可能找到
产生空值读取,它
可以证明没有存在(和
也许有一天有一天JLS / JMM修订版
将填补空白,以澄清这一点),但是Bill Pugh曾经建议我们把
放在这里只是为了
正确保守地
。回想起来,我不是这样的
肯定这是一个好主意,因为它
引导人们拿出异国情调的
理论。


它可以全部查看这里


I'm looking into ConcurrentHashMap implementation and have a thing make me confused.

/* 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;
        }

and

    /**
     * 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();
        }
    }

and HashEntry constructor

/**
     * 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;
            }

put implement

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

I confused at HashEntry comment, as JSR-133, once HashEntry is constructed, all final fields will be visible to all other threads, value field is volatile, so I think it visible to other threads too??? . Other point, is the reorder he said is: HashEntry object reference can be assigned to tab[...] before it is full constructed (so result is other threads can see this entry but e.value can be null) ?

Update: I read this article and it's good. But do I need to care about a case like this

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

thread1:

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

thread2:
Person p = queue.poll();

Is there a chance that thread2 receive an unfinished-construct Person object just like HashEntry in

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

解决方案

For those interested in an answer from the Doug Lea on this topic, he recently exlpained the reason for readValueUnderLock

This is in response to someone who had the question:

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.

Response:

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.

It can all be viewed here

这篇关于ConcurrentHashMap重新排序指令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆