4
  • 私を困惑させるのはこれです。

ConcurrentHashMap の HashEntry の Java ドキュメント (jdk1.6.0_16)

...値フィールドは最終的なものではなく揮発性であるため、データ競合を介して読み取られたときに、非同期リーダーが初期値ではなく null を表示することは、Java メモリ モデルに関して合法です。これにつながる並べ替えが実際に発生する可能性はほとんどありませんが、Segment.readValueUnderLock メソッドは、同期されていないアクセス メソッドで null (事前に初期化された) 値が見られる場合のバックアップとして使用されます。

  • ここに ConcurrentHashMap#Segment の get メソッドの実装があります

    
    V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry 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;
        }
    
  • そして readValueUnderLock の


V readValueUnderLock(HashEntry e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }
  • 私の読書と理解に基づいて、すべてのスレッドは揮発性変数の最新の値を読み取ります。

  • では、スレッドはいつ最初の null 値を読み取るのでしょうか? 特に、コンストラクターが完了する前に値が割り当てられる HashEntry では。(HashEntry の参照がそのコンストラクターを決してエスケープしないことにも注意してください。)

  • ConcurrentHashMap(jdk1.6.0_16)のHashEntryの上記のJavaドキュメントを説明できる人がいます。そして、なぜ特別な予防ロックが必要なのですか?

4

2 に答える 2

3

Java 1.5 がリリースされたとき、HashEntry を部分的に初期化できるという規定が JMM にありました。つまり、スレッドがマップに挿入されると、HashEntry が作成され、バケット ヘッドまたは衝突メンバーのいずれかへの参照として割り当てられます。その時点で、エントリの値は、他のスレッドから見えるように割り当てられていない可能性があります。

CHM は、エントリが null でない場合、値が null であってはならないと想定しているため、readValueUnderLock がフェイルセーフとして配置されました。

私はこの正確な状況について DL に尋ねました。彼はまた、1.6 以降、その問題は起こらないとも言いました。

于 2010-12-03T15:23:12.577 に答える
0

コンストラクターが完了する前に、マップへの参照が誰にも使用できないことを確認する必要があります。それがプライベート フィールドであり、getter メソッドによってのみアクセスする場合 (そのフィールドが保持されているクラスを含む)、これは簡単に実行できるはずです。

インスタンスの getter メソッドが呼び出される前に、コンストラクターが終了します。

于 2010-12-03T07:50:47.850 に答える