ファイル システムのリソースを利用する高度な同時実行アプリケーションがあります。2 つのスレッドが同じリソースに同時にアクセスする可能性はかなり低いですが、これが発生した場合、アプリケーションはワイヤード動作を示す可能性があります。
各リソースは、座標のベクトルによってマップできますString
(クラスにバンドルされていResourceIdentifier
ます)。私の現在のソリューションではConcurrentMap
、リソースにアクセスするときにスレッドによって使用されるモニターを収集するために、そのようなリソース識別子を作成しました: (ResourceIdentifier
オーバーライドequals
してhashCode
正しく。)
ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap
= new ConcurrentHashMap<>();
public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
return concurrentMap.get(resourceIdentifier);
}
リソースがアクセスされると、 によって返される監視オブジェクトへのアクセスを同期しますaquireMonitor
。の実装を理解している限りConcurrentHashMap
、これは必ずしもすべてのスレッドをブロックするわけではなく (実装を理解するためにこのブログ記事を読みました)、以前紹介したリソースの 1 つに同時アクセスする危険なしに、アプリケーションを問題なく実行できます。非常にまれな機会に醜いバグ。
ただし、私のアプリケーションは多数のリソースを管理しておりconcurrentMap
、実行時に増加します。これが、(Guava を使用して) アプリケーションに弱参照セマンティクスを追加しようとする理由です。
ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap
= new MapBuilder().weakValues().weakKeys()
.concurrencyLevel(CONCURRENCY_LEVEL).makeMap();
public Object aquireMonitor(ResourceIdentifier resourceIdentifier) {
ResourceIdentifier monitor;
do {
concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier);
monitor = concurrentMap.get(resourceIdentifier);
} while(monitor == null);
return monitor;
}
CONCURRENCY_LEVEL
もちろん静的フィールドです。
私の考えは次のようなものでした: モニターがまだ別のスレッドで使用されているときはいつでも、もちろんこのモニターへの (強力な) 参照を保持します。したがって、 のエントリはConcurrentMap
ガベージ コレクションされず、2 つのスレッドが同じリソースにアクセスするときにモニターが共有されることが保証されます。(ループは、 と の呼び出しの間で発生する可能性のあるガベージ コレクションに対処putIfAbsent
しますget
。)
ただし、MapMaker.weakKeys
エントリが見つかるという契約を破り、equals
代わりに ID を使用します。
今私は疑問に思っています: 誰かここからどこへ行くべきか知っていますか? それとも、このアプローチはとにかく悪い考えですか? 副次的な質問として: のみを使用した場合、エントリ全体がマップから削除されweakValues
ますか? それとも、マップは常にそのキーによって別の強力な参照を持っていますか? 手伝ってくれてありがとう!
PS: 私の最初の推測では、マップからキャッシュに移行する必要があります。これはおそらく最良の解決策ですか?これまで Guava を使用したことはありませんでしたが、今のところ、キャッシュのキー比較で同じ制限が見つかりました。
PPS: ファイル システムにロックを作成できません。(私の電話ではありません。)