0

同じマップ上の要素を処理するスレッドが多数あり、それぞれにコードがあります。次に例を示します。

THREAD_1 works on an element with code "1234"
THREAD_2 works on an element with code "1234"
THREAD_3 works on an element with code "9876"
etc...

要素は永続的ではありません。つまり、THREAD_1は要素「1234」を削除してから再度挿入する場合があります。私が欲しいのは、THREAD_1が要素 "1234"で作業している間(またそれを削除している間)、THREAD_2は待機しなければならないということです。

それを行う方法はありますか?

考えられる解決策は、HashMapに偽の要素を挿入してから、その要素に「synchronized」句を使用して同期を強制することです。についてどう思いますか?(明らかに、スレッドが関連するコードで要素を削除した場合でも、その偽の要素はマップに残ります)...

4

3 に答える 3

2

特定の問題を考えると、Java標準オブジェクトのどれもすべての問題を解決することはできません。これが正しいと私が信じる解決策であり、ロックマップに不要なキーや値を保持しません。

// we don't use a ConcurrentHashMap, because we have some other operations 
// that need to be performed in atomically with map.put and map.remove.
// ConcurrentHashMap would of course also work, but it doesn't remove the 
// need for external synchronization in in our case.
Map<String, CountingLock> locksMap = new HashMap<String, CountingLock>();
...

HttpResponse myFunction(String key) {

    CountingLock lock;
    synchronized(locksMap){
        lock = locksMap.get(key);
        if(lock == null){
            lock = new CountingLock();
            locksMap.put(key, lock);
        }
        lock.prepare(); // has to be done while holding the lock of locksMap.
                        // basically tells other threads that the current 
                        // thread intends to acquire the lock soon. This way,
                        // the other threads know not to remove this lock 
                        // from locksMap as long as another one has indicated
                        // that he is going to need it soon.
    }

    lock.lock(); // has to be done while NOT holding the lock of locksMap,
                 // or we risk deadlock situations.

    try {
        // ...
        // work
        // ...
    } finally {
        synchronized(locksMap) {
            if(lock.unlock() == 0){
                // no other thread is intending to use this lock any more. 
                // It is safe to remove it from the map. The next thread 
                // will just have to recreate a new lock for the same key.
                locksMap.remove(key);
            }
        }
    }

    return SOMETHING;    
}

private static class CountingLock {
    // The number of threads that are trying to access the protected Key
    private AtomicInteger interestedThreads = new AtomicInteger(0);

    private Lock lock = new ReentrantLock();

    public void prepare(){
        interestedThreads.incrementAndGet();
    }

    public void lock(){
        lock.lock();
    }

    public int unlock(){
        lock.unlock();
        return interestedThreads.decrementAndGet();              
    }
}

このコードは、すべての場合に期待どおりに機能するはずです。それは解決するのが楽しい問題でした:-)

于 2012-11-02T23:19:39.823 に答える
1

LockMap と呼ばれるものを使用します。

LockMap は本質的に次のとおりです。

Map<Object, ReadWriteLock>

特定のオブジェクトのロックを取得する同期メソッドがあります。

マップは同一性ではなく等価性に依存しているため、2 つのオブジェクトがequal()同じロックを提供します。

そう:

lock1 = map.get(new Thing(1));
lock2 = map.get(new Thing(1));

lock1 == lock2 = true

これは便利です。

ロックを取得したら、オブジェクトへのアクセスを制御したいときにロックできます。

私たちが行うもう 1 つのことは、LRU マップを使用することです (LinkedHashMap を使用します。これを参照してください)。これにより、古いオブジェクト キーは、使用されていないときに末尾から外れることがあります。

于 2012-11-02T21:21:55.213 に答える
0

あなたは使用する必要がありますConcurrentHashMap

取得の完全な同時実行性と更新の調整可能な予想同時実行性をサポートするハッシュテーブル。

取得操作(getを含む)は通常ブロックされないため、更新操作(putおよびremoveを含む)と重複する場合があります。

更新操作間で許可される同時実行性は、オプションのconcurrencyLevelコンストラクター引数(デフォルトは16)によって導かれます。これは、内部サイズ設定のヒントとして使用されます。テーブルは内部でパーティション化されており、競合することなく、指定された数の同時更新を許可しようとします。ハッシュテーブルへの配置は基本的にランダムであるため、実際の同時実行性は異なります

于 2012-11-02T18:43:42.923 に答える