やっかいなことHashtable
をした後Vector
、Collections同期ラッパーが登場したとき、同期はより効率的に処理されると思いました。コードを調べたところ、コレクションが同期ブロックでラップされているだけであることに驚いています。
ReadWriteLock
たとえば、コレクションのSynchronizedMapにが含まれていないのはなぜですか?それだけの価値がない効率性の考慮事項はありますか?
やっかいなことHashtable
をした後Vector
、Collections同期ラッパーが登場したとき、同期はより効率的に処理されると思いました。コードを調べたところ、コレクションが同期ブロックでラップされているだけであることに驚いています。
ReadWriteLock
たとえば、コレクションのSynchronizedMapにが含まれていないのはなぜですか?それだけの価値がない効率性の考慮事項はありますか?
読み取り/書き込みロックはパフォーマンス最適化の一部です。つまり、特定の状況でより大きな同時実行性を実現できます。必要な条件は、ほとんどの場合読み取られるが変更されないデータ構造に適用されることです。
他の条件下では、排他ロックよりもわずかにパフォーマンスが低下します。これは、複雑さが増すため、当然のことです。
読み取り/書き込みロックのロックが通常は適度に長い時間保持され、保護されたリソースへの変更がほとんどない場合は、最も効率的です。
したがって、読み取り/書き込みロックが排他ロックよりも優れているかどうかは、ユースケースによって異なります。最終的には、どのロックのパフォーマンスが向上するかをプロファイリングで測定する必要があります。
これを考慮に入れるとCollections.synchronizedMap
、ほとんどの読者がいる特別なケースではなく、一般的なユースケースに対処するための専用ロックを選択するのが適切だと思われます。
その他のリンク
彼らは、Javaアプリケーションのロックを適切な場所に自動的ReentrantLocks
に変換するツールを作成しました。ReadWriteLocks
測定の目的で、彼らはいくつかの興味深いベンチマーク結果も提供しています。
[...]ただし、書き込み操作がより一般的な構成では、同期ブロックを使用するバージョンは、Sun 1.6.0_07 JVMを使用した読み取り/書き込みロックに基づくバージョンよりも50%高速でした(Sun 1.5を使用すると14%高速でした)。 0_15 JVM)。
リーダーとライターが1つしかない競合の少ないケースでは、パフォーマンスの違いはそれほど極端ではなく、3種類のロックのそれぞれが、少なくとも1つのマシン/ VM構成で最速のバージョンを生成しました(たとえば、ReentrantLocksはSun 1.5.0_15 JVMを搭載した2コアマシン)。
キーワードReadWriteLock
を使用するよりも(それがあなたが話しているのであれば)使用する方が必ずしも速いとは思いません。synchronized
どちらの構成も、ロックを課し、メモリバリアを構築し、「前に発生する」制限を課します。
Collections.synchronizedMap(...)
あなたは、パフォーマンスのために読み取りメソッドが読み取りロックされ、書き込みメソッドが書き込みロックされるというスマートなことを話しているかもしれません。これはコレクションクラスでは正常に機能する可能性がありますが、メソッドがカウントされている場合など、ユーザーによって実装されたsとのjava.util
同期の問題を引き起こす可能性があります。つまり、「読み取り専用」のメソッドが実際にコレクションを更新した場合です。はい、これを行うのはひどい考えです。Map
get()
ConcurrentHashmap
高性能であるように作成され、ブロックvolatile
の代わりにフィールドを直接使用します。synchronized
これにより、コードは比較して大幅に複雑にCollections.synchronizedMap(...)
なりますが、高速になります。これが、以上の高性能な状況で推奨される理由Collections.synchronizedMap(new HashMap<...>())
です。
以下を除いて、ほとんどの理由が取り上げられています。SynchronizedMap / Set / List、およびHashtableとVectorは、同期されているコレクションインスタンスに依存しています。その結果、多くの開発者がこの同期を使用して原子性を確保しています。例えば。
List syncList = Collections.synchronizedList(new ArrayList());
//put if absent
synchronized(syncList){
if(!syncList.contains(someObject)){
syncList.add(someObject);
}
}
これは、synchronizedListがそれ自体で同期するため(つまり、add、remove、get)、すべての操作でスレッドセーフでアトミックです。これが、HashtableクラスがConcurrentHashMapと同様のロックストライピングをサポートするように改良されなかった主な理由です。
したがって、これらのコレクションにReadWriteLockを使用すると、ロックインスタンスを取得できない限り、アトミック操作の機能が失われます。
これは、これらのクラスが、Javaにかなり遅れて導入された読み取り/書き込みロック(Java 5)よりも前から存在しているためです。とにかく、ハードコーディングされたきめ細かいロックのため、これらが役立つことはめったにありません。
それは効率については何もありません。synchronized
で使用するのは問題ありませんCollections.synchronizedMap
。ReadWriteLockを使用してマップを実装する場合は、それを呼び出したいと思い Collections.LockedMap
ます;)
真剣に、Collections.synchronizedMap
何年も前に書かれましたLock
。リリース後に変更できないAPIです。
@JohnVintの答えに追加するには、反復の問題を検討してください。ドキュメントでは、クライアントの同期が明示的に必要です。
返されたリストを反復処理するときは、ユーザーが手動でリストを同期する必要があります。
List list = Collections.synchronizedList(new ArrayList()); ... synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
このアドバイスに従わないと、非決定論的な動作が発生する可能性があります。
これが機能するのは、クライアントが返されたマップの固有のロックを共有できるためです。内部で読み取り/書き込みロックを使用する場合、返されるインターフェイスは、安全な反復のために、少なくとも読み取りロックにアクセスする何らかの方法をサポートする必要があります。それは疑わしい利益のためにAPIを複雑にするでしょう(他の人が説明しているように)。