1

Я ищу способ отслеживать количество попыток одной и той же ключевой вставки в Map в многопотоковой среде, так что Map может быть чтение и обновление несколькими потоками одновременно. Если отслеживание попыток дублирования ключевых вставок невозможно достичь, альтернативным решением было бы убить приложение при первом знаке попытки повторной вставки ключа.Сохранение дорожки дублированных вставок на карте (многопоточная среда)

Следующий пользовательский синглтон Spring bean показывает глобальный кеш, используемый моим приложением, который загружается с использованием нескольких распределенных заданий весовой партии (одно задание для каждого загружаемого DataType). Метод addResultForDataType может быть вызван несколькими потоками одновременно.

public class JobResults { 

    private Map<DataType, Map<String, Object>> results; 

    public JobResults() { 
     results = new ConcurrentHashMap<DataType, Map<String, Object>>(); 
    } 

    public void addResultForDataType(DataType dataType, String uniqueId, Object result) { 
     Map<String, Object> dataTypeMap = results.get(dataType); 
     if (dataTypeMap == null) { 
      synchronized (dataType) { 
       dataTypeMap = results.get(dataType); 
       if (dataTypeMap == null) { 
        dataTypeMap = new ConcurrentHashMap<String, Object>(); 
        results.put(dataType, dataTypeMap); 
       } 
      } 
     } 
     dataTypeMap.put(uniqueId, result); 
    } 

    public Map<String, Object> getResultForDataType(DataType dataType) { 
     return results.get(dataType); 
    } 

} 

Здесь:

  • DataType можно рассматривать как имя таблицы или имя файла из , где данные загружаются. Каждый DataType указывает одну таблицу или файл.
  • uniqueId представляет первичный ключ для каждой записи в таблице или файле.
  • result - объект, представляющий всю строку.
  • Вышеуказанный метод вызывается один раз за запись. В любой момент времени несколько потоков могут вставлять запись для одного и того же DataType или другого DataType.

Я думал о создании другой карты, чтобы сохранить трек из дублирующих вставок:

public class JobResults { 

    private Map<DataType, Map<String, Object>> results; 
    private Map<DataType, ConcurrentHashMap<String, Integer>> duplicates; 

    public JobResults() { 
     results = new ConcurrentHashMap<DataType, Map<String, Object>>(); 
     duplicates = new ConcurrentHashMap<DataType, ConcurrentHashMap<String, Integer>>(); 
    } 

    public void addResultForDataType(DataType dataType, String uniqueId, Object result) { 
     Map<String, Object> dataTypeMap = results.get(dataType); 
     ConcurrentHashMap<String,Integer> duplicateCount = duplicates.get(dataType); 
     if (dataTypeMap == null) { 
      synchronized (dataType) { 
       dataTypeMap = results.get(dataType); 
       if (dataTypeMap == null) { 
        dataTypeMap = new ConcurrentHashMap<String, Object>(); 
        duplicateCount = new ConcurrentHashMap<String, Integer>(); 
        results.put(dataType, dataTypeMap); 
        duplicates.put(dataType, duplicateCount); 
       } 
      } 
     } 
     duplicateCount.putIfAbsent(uniqueId, 0); 
     duplicateCount.put(uniqueId, duplicateCount.get(uniqueId)+1);//keep track of duplicate rows 
     dataTypeMap.put(uniqueId, result); 
    } 

    public Map<String, Object> getResultForDataType(DataType dataType) { 
     return results.get(dataType); 
    } 

} 

Я понимаю, что statemet duplicateCount.put(uniqueId, duplicateCount.get(uniqueId)+1); не неявно поточно. Чтобы сделать его потокобезопасным, мне нужно будет использовать синхронизацию, которая замедлит мои вставки. Как я могу отслеживать дубликаты вставок, не влияя на производительность моего приложения. Если отслеживать повторяющиеся вставки нелегко, мне было бы хорошо сбрасывать исключение при первом признаке попытки перезаписать существующую запись на карте.

Примечание Я знаю, что Map не позволяет дублировать ключи. Я хочу, чтобы отслеживать любые попытки и останавливать приложение, а не перезаписывать записи в Map.

+0

вы хотите, чтобы избежать дубликатов на внешней или внутренней 'Map' – Actorclavilis

+0

@Actorclavilis Только внешняя карта. (См. Мою попытку решения). – Ping

ответ

1

попробовать что-то вроде этого:

ConcurrentHashMap<String, AtomicInteger> duplicateCount = new ConcurrentHashMap<String, AtomicInteger>(); 

Затем, когда вы будете готовы, чтобы увеличить счетчик, сделайте следующее:

final AtomicInteger oldCount = duplicateCount.putIfAbsent(uniqueId, new AtomicInteger(1)); 
if (oldCount != null) { 
    oldCount.incrementAndGet(); 
} 

Так что, если у вас нет счета в карте еще , вы положите 1, если у вас есть, вы получите текущее значение и атомарно увеличьте его. Это должно быть потокобезопасным.

+1

Использование атома один за другим не делает все атомом – UmNyobe

+0

Он либо ставит новый AtomicInteger со значением 1, либо увеличивает существующий AtomicInteger. Он не использует оба одновременно. Я думаю, вы всегда можете сделать что-то вроде этого: final AtomicInteger oldCount = duplicateCount.putIfAbsent (uniqueId, новый AtomicInteger (1)); если (старыйCount! = Null) { старыйCount.incrementAndGet(); } –

+0

Предостережение в вашем коде заключается в том, что между чтением oldCount и выполнением нулевой проверки не существует отношения «произойдет-до». Очевидно, что в нынешнем контексте - это не имеет значения, но стоит все же назвать его. – kgdinesh

0

Если вы хотите, чтобы следить за количеством вставок, вы можете изменить внешний вид карты, чтобы что-то вроде Map<String, Pair<Integer, Object>> (или, если вы не используете Apache Commons, просто Map<DataType, Map.Entry<Integer, InnerType>>, где значение Integer является число обновления:

DataType key = ...; 
Map<Integer, Object> value = ...; 
dataTypeMap.compute(key, (k, current) -> { 
    if (current == null) { 
     /* Initial count is 0 */ 
     return Pair.of(0, value); 
    } else { 
     /* Increment count */ 
     return Pair.of(current.getFirst(), value); 
    })); 

Если все, что вы заботитесь о гарантирует, что нет повторяющихся вставок, вы можете просто использовать computeIfAbsent:

DataType key = ...; 
Map<Integer, Object> value = ...; 
if (dataTypeMap.computeIfAbsent(key, k -> value)) != null) { 
    /* There was already a value */ 
    throw new IllegalStateException(...); 
}); 
+0

Спасибо за ответ; однако обратите внимание, что изменение структуры «Карты» не является вариантом. – Ping