私の知る限り、 _Java.util.Hashtable
_ は _Java.util.Map
_ インターフェイスの各メソッドを同期しますが、 Collections.synchronizedMap(hash_map)
は、実際の_hash_map
_への呼び出しを委任する同期メソッドを含むラッパーオブジェクトを返します(間違っている場合は修正してください)。
2つの質問があります:
すべてのメソッドを同期し、ラッパークラスを作成すると、どのような違いが生じますか?どちらを選択するシナリオは何ですか?
Collections.synchronizedMap(hash_table)
を実行するとどうなりますか?これは、通常の_Java.util.Hashtable
_を単に使用するのと同じでしょうか?
少しの(うまくいけば正しい)研究から得た答えは次のとおりです。
どちらも同程度の同期を提供します。 Collections.synchronizedを介してHashtable
をラップする場合、同期の度合いは同じですが、別の冗長レイヤーがあります。
Hashtable
とCollections.synchronizedMap(HashMap)
の主な違いは、APIレベルでより多く存在します。 Hashtable
はJavaのレガシーコードの一部であるため、Hashtable
APIが拡張されてMap
インターフェイスを実装し、Javaのコレクションフレームワークの一部になることがわかります。つまり、Hashtable
をCollections.synchronizedMap()
でラップすると、ラップされたHashtable
のAPIはMap
APIに制限されることになります。したがって、Hashtable
のAPIがbehaviorの定義に含まれている場合、明らかに変更または制限されます。
両方のクラスの実装で見つけることができるもう1つの違いは、次のとおりです。
•Hashtable
クラスにはすべてのメソッドが同期されています。つまり、ロックはメソッドレベルで実行されるため、mutexはalwaysHashtable
オブジェクト(-this
)レベルであると言えます。 。
•Collections.synchronizedMap(Map)
メソッドは、SynchronizedMap
クラスの内部クラスであるCollections
のインスタンスを返します。このクラスのすべてのメソッドは、mutexを使用したSynchronized
ブロックにあります。ここでの違いはミューテックスにあります。内部クラスSynchronizedMap
には2つのコンストラクターがあり、1つは引数としてMap
のみを受け取り、もう1つは引数としてMap
とObject
(mutex)を受け取ります。デフォルトでは、Map
のみを渡す最初のコンストラクターを使用する場合、this
がミューテックスとして使用されます。ただし、開発者は、Map
メソッドのロックがそのObject
に対してのみ行われるため、Hashtable
よりも制限が少ない別のmutexオブジェクトを第2引数として渡すことができます。
•したがって、Hashtable
はメソッドレベルの同期を使用しますが、Collections.synchronizedMap(Map)
は、Synchronized
ブロックで提供されたミューテックスを開発者がロックする柔軟性を提供します。
Javaクラスライブラリに表示される最初の連想コレクションクラスは、JDK 1.0の一部であるHashtableでした。Hashtableは、使いやすく、スレッドセーフで、連想マップ機能を提供しました。ただし、Hashtableのすべてのメソッドは同期されていましたが、競合しない同期には、測定可能なパフォーマンスコストがありました。Hashtableの後継であるHashMapは、Collectionsフレームワークの一部として登場しましたJDK 1.2では、非同期の基本クラスと同期ラッパー、Collections.synchronizedMapを提供することでスレッドセーフに対応しました。スレッドセーフから基本機能を分離するCollections.synchronizedMapにより、同期が必要なユーザーはそれを使用できますそれを支払う必要はありませんでした必要があります。
HashtableとsynchronizedMapの両方で行われる同期への単純なアプローチ(Hashtableまたはsynchronized Mapラッパーオブジェクトの各メソッドの同期)には、2つの主な欠点があります。一度に1つのスレッドしかハッシュテーブルにアクセスできないため、これはスケーラビリティの障害です。同時に、多くの一般的な複合操作では追加の同期が必要になるため、真のスレッドセーフティを提供するには不十分です。 get()やput()などの単純な操作は、追加の同期なしで安全に完了することができますが、反復やput-if-absentなどの一般的な操作シーケンスがいくつかあり、データ競合を避けるために外部同期が必要です。
次のリンクはソースであり、詳細情報があります。 Concurrent Collections Classes
違いは明らかなAPIレベルですべてではなく、実装レベルでは多くの微妙な点があります。たとえば、Hashtable
は、HashMap
の提供されたキーのハッシュコードの高度な再計算を行わず、ハッシュの衝突を減らします。一方、Hashtable#hashCode()
は、自己参照ハッシュテーブルの無限再帰を回避して、「自己参照ハッシュテーブルを備えた特定の1.1時代のアプレットが機能する」ようにします。
ただし、一般的には、Hashtable
が基本的な正確性と後方互換性を超えたさらなる改善または改良を受け取ることを期待するべきではありません。深いJava過去の遺物と見なされます。
注意すべきもう1つの違いは、-HashTableはnullキーまたはnull値を許可しないのに対して、-HashMapは1つのnullキーと任意の数のnull値を許可することです。 synchronizedMapはHashMapのラッパーであるため、nullキーと値に関する動作はHashMapと同じです。
明白な(または明白に間違っている)と述べるリスクは、以下の違いではありません。
同期ラッパーは、自動同期(スレッドセーフ)を任意のコレクションに追加します
http://docs.Oracle.com/javase/tutorial/collections/implementations/wrapper.html そして言い続ける
この方法で作成されたコレクションは、ベクターなどの通常同期されたコレクションと同じくらいスレッドセーフです。
HashMapと並行性に関する問題については、このスレッドをご覧ください- Hashmap並行性の問題 良い例は次のとおりです。
あなたが説明する条件は、HashMapによって満たされません。マップの更新プロセスはアトミックではないため、マップが無効な状態になる可能性があります。複数の書き込みを行うと、破損状態のままになる可能性があります。 ConcurrentHashMap(1.5以降)はあなたが望むことをします。
https://stackoverflow.com/a/1003071/201648
「これをいつ使うべきか」という点では、同時実行性が必要な場合は同期化されたコレクションを使用する傾向があると思います。
動作の変更に関して
明示的な反復子を使用する場合は、同期ブロック内から反復子メソッドを呼び出す必要があります。このアドバイスに従わないと、非決定的な動作が発生する場合があります
提供されている(Oracle)リンクで指定された同期を使用すると、さらに多くの結果が生じます。