6

ファイル システムのリソースを利用する高度な同時実行アプリケーションがあります。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: ファイル システムにロックを作成できません。(私の電話ではありません。)

4

2 に答える 2

6

キーと値の両方に弱参照が必要です。

キャッシュに切り替えることをお勧めします。または、それを禁止して、SoftReferences に切り替えることをお勧めしますConcurrentMap。GC弱い参照の収集に熱心であるため、弱い参照の収集にはあまり適していません。まだそれらが原因を許可していませんOutOfMemoryError。ソフト参照の同時実行マップを実装するには、ConcurrentMapラッパーを作成します。

class SoftConcurrentMap<K, V> extends ConcurrentHashMap<SoftReference<K>, SoftReference<V>> {
    ConcurrentHashMap<SoftReference<K>, SoftReference<V>> map = new ConcurrentHashMap<>();

    V public void get(Object key) {
        SoftReference<V> value = map.get(new SoftRefrence(key));
        if(value != null && value.get() != null) {
            return value.get();
        } else {
            map.remove(new SoftReference(key));
            return null;
        }
    }

    V put(K key, V value) {
        SoftReference<V> oldValue = map.put(new SoftReference(key), new SoftReference(value));
        return oldValue == null ? null : oldValue.get();
    }
}

等々。これは多くのメソッド ラップ スルーなので、代わりにEHCacheなどを使用することをお勧めします。

于 2013-07-01T14:22:37.777 に答える