6

が同期されていることはわかっていHashtableますが、そのget()メソッドが同期されているのはなぜですか?

読み取り方法だけですか?

4

3 に答える 3

16

読み取りが同期されていない場合、読み取りの実行中に Hashtable が変更される可能性があります。新しい要素が追加されたり、基になる配列が小さすぎて、より大きな配列に置き換えられたりする可能性があります。順次実行しないと、これらの状況に対処することは困難です。

ただし、getHashtable が別のスレッドによって変更されたときにクラッシュしない場合でも、synchronizedキーワードにはもう 1 つの重要な側面があります。つまり、キャッシュの同期です。簡単な例を使用してみましょう。

class Flag {
  bool value;

  bool get() { return value; } // WARNING: not synchronized
  synchronized void set(bool value) { this->value = value; }
}

set同期されていますが、getそうではありません。このクラスに対して 2 つのスレッド A と B が同時に読み取りと書き込みを行うとどうなりますか?

1. A calls read
2.                 B calls set
3. A calls read

ステップ 3 で、A がスレッド B の変更を確認することが保証されますか?

A は、古い値がまだ存在する別のキャッシュを使用する別のコアで実行されている可能性があるためです。したがって、B にメモリを他のコアと通信させ、A に新しいデータをフェッチさせる必要があります。

どうすればそれを強制できますか? スレッドが同期ブロックに出入りするたびに、暗黙的なメモリ バリアが実行されます。メモリ バリアにより、キャッシュが強制的に更新されます。ただし、ライターとリーダーの両方がメモリ バリアを実行する必要があります。そうしないと、情報が適切に伝達されません。

この例では、スレッド B はすでに同期メソッドを使用しているsetため、そのデータ変更はメソッドの最後で伝達されます。ただし、A には変更されたデータが表示されません。解決策はget同期化することであり、更新されたデータを強制的に取得する必要があります。

于 2013-01-14T02:54:54.737 に答える
1

ハッシュテーブルのソースコードを見ると、非同期で問題を引き起こす可能性のある多くの競合状態を考えることができますget()

(私はJDK6のソースコードを読んでいます)

たとえば、arehash()は空の配列を作成し、それをインスタンスvarに割り当て、table古いテーブルのエントリを新しいテーブルに配置します。したがってget、空の配列割り当ての後で、実際にエントリを配置する前に発生した場合は、テーブルにある場合でもキーを見つけることができません。

もう1つの例は、テーブルインデックスのリンクリストを反復するループがあり、反復の途中で再ハッシュが発生した場合です。また、ハッシュテーブルにエントリが存在していても、エントリが見つからない場合があります。

于 2013-01-14T02:32:40.847 に答える
0

Hashtable同期されているということは、クラス全体がスレッドセーフであることを意味します

内部ではHashtable、get()メソッドが同期されるだけでなく、他の多くのメソッドも同期されます。そして特にput()メソッドはトムが言ったように同期されます。

読み取りメソッドは、変数の可視性と一貫性を確保するため、書き込みメソッドとして同期する必要があります。

于 2013-01-14T02:12:03.913 に答える