次のコードは、スレッドセーフであります

何:

私は私が複数のスレッドによって移入することができる地図を維持しなければならないシナリオ、それぞれがそれぞれのリストを変更する(一意の識別子/キービーイングスレッド名)を有し、スレッドのリストのサイズが固定されたバッチ・サイズを超えた場合、我々は存続しなければなりませんDB内のレコード。

以下のサンプルコード:

private volatile ConcurrentHashMap<String, List<T>>  instrumentMap = new ConcurrentHashMap<String, List<T>>();
private ReadWriteLock lock ;

public void addAll(List<T> entityList, String threadName) {
    try {
        lock.readLock().lock();
        List<T> instrumentList = instrumentMap.get(threadName);
        if(instrumentList == null) {
            instrumentList = new ArrayList<T>(batchSize);
            instrumentMap.put(threadName, instrumentList);
        }

        if(instrumentList.size() >= batchSize -1){
            instrumentList.addAll(entityList);
            recordSaver.persist(instrumentList); 
            instrumentList.clear();
        } else {
            instrumentList.addAll(entityList);  
        }
    } finally {
        lock.readLock().unlock();
    }

}

(そしてそれが起動したとき、それは他のすべてのスレッドをブロック(必ず私たちは何かがおき2分後に持続し、マップのサイズが大きくなりすぎていません持たせる)地図のチェックをすべてのレコードを保持するために、すべての2分後に実行されているもう一つの別のスレッドがありますreadLockとWRITELOCK usawhere WRITELOCK)が優先順位が高いです

if(//Some condition) {
                    Thread.sleep(//2 minutes);
                    aggregator.getLock().writeLock().lock();
                    List<T> instrumentList = instrumentMap .values().stream().flatMap(x->x.stream()).collect(Collectors.toList());
                    if(instrumentList.size() > 0) {

                        saver.persist(instrumentList);
                        instrumentMap .values().parallelStream().forEach(x -> x.clear());
                    aggregator.getLock().writeLock().unlock();
                }

このソリューションは、ほとんど私たちは、彼らが地図に罰金を追加しましたが、レコードの一部は、すなわち、すべての永続化されません行方不明に見我々はいつか除いテストすべてのシナリオのための罰金を働いています

私の質問は、このコードに問題がある何ですか?ConcurrentHashMapのは、ここでは最善の解決策ではないですか?読み取り/書き込みロックの使用が、ここでいくつかの問題を持っていますか?私は逐次処理で行くべきでしょうか?

アンディ・ターナー:

いいえ、それはスレッドセーフではありません。

問題は、あなたが使用していることである読み取り ReadWriteLockのロックを。これは、更新を行うための排他的アクセスを保証するものではありません。あなたは使用する必要があると思い書き込み、そのためにロックを。

しかし、あなたは本当にすべての個別のロックを使用する必要はありません。あなたは、単に使用することができますConcurrentHashMap.compute方法を:

instrumentMap.compute(threadName, (tn, instrumentList) -> {
  if (instrumentList == null) {
    instrumentList = new ArrayList<>();
  }

  if(instrumentList.size() >= batchSize -1) {
    instrumentList.addAll(entityList); 
    recordSaver.persist(instrumentList); 
    instrumentList.clear();
  } else {
    instrumentList.addAll(entityList);
  }

  return instrumentList;
});

これは、あなたはまた、指定されたキーのリストへの排他的アクセスを保証しながら、リスト内の項目を更新することができます。

私はあなたが分割することができることを疑うcomputeにコールをcomputeIfAbsent(1が存在しない場合は、リストを追加するために)、その後computeIfPresent(更新に/リストを永続化する):これら二つの操作のアトミック性は、ここでは必要ありません。しかし、それらを分割するには本当の意味がありません。


また、instrumentMapほぼ確実に揮発性であってはなりません。あなたは本当に、その価値を再割り当てする場合を除き(このコードを与え、私はそれを疑う)、揮発削除し、それが最終的に作ります。

同様に、非最終ロックはあまりにも疑問です。あなたがロックを使用してに固執する場合は、あまりにもそれが最終的にします。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=201115&siteId=1