2016-01-15 2 views
0

необходимо предоставить поточно-реализацию следующего контейнера:Делегирования потокобезопасности к ConcurrentMap и AtomicInteger

public interface ParameterMetaData<ValueType> { 
    public String getName(); 
} 

public interface Parameters { 
    public <M> M getValue(ParameterMetaData<M> pmd); 
    public <M> void put(ParameterMetaData<M> p, M value); 
    public int size(); 
} 

Вещь метод размера должен вернуть точного количество paramters, содержащееся в настоящее время в экземпляре Parameters. Итак, моя первая попытка была попытка делегировании потокобезопасности следующим образом:

public final class ConcurrentParameters implements Parameters{ 

    private final ConcurrentMap<ParameterMetaData<?>, Object> parameters = 
                new ConcurrentHashMap<>(); 
    //Should represent the ACCURATE size of the internal map 
    private final AtomicInteger size = new AtomicInteger(); 

    @Override 
    public <M> M getValue(ParameterMetaData<M> pmd) { 
     @SuppressWarnings("unchecked") 
     M value = (M) parameters.get(pmd); 
     return value; 
    } 

    @Override 
    public <M> void put(ParameterMetaData<M> p, M value){ 
     if(value == null) 
      return; 
     //The problem is in the code below 
     M previous = (M) parameters.putIfAbsent(p, value); 
     if(previous != null) 
      //throw an exception indicating that the parameter already exists 
     size.incrementAndGet(); 
    } 

    @Override 
    public int size() { 
     return size.intValue(); 
    } 

Проблема заключается в том, что я не могу просто назвать parameters.size() на экземпляре ConcurrentHashMap вернуть фактический размер, как операция выполняет обход без и нет гарантии, что он будет получать фактический размер. В моем случае это неприемлемо. Итак, я решил сохранить поле, содержащее размер.

ВОПРОС: Можно ли каким-то образом делегировать безопасность потока и сохранить invariatns?

+0

Без внешней блокировки вы не можете получить более высокую точность, чем ConcurrentHashMap уже предоставляет, так как вы не можете гарантировать атомические операции. – Ferrybig

ответ

4

Результат, который вы хотите достичь, не является атомным. Вы хотите изменить карту, а затем получить количество элементов, которые были бы согласованы в области одного потока. Единственный способ добиться этого - сделать этот поток «атомной операцией», синхронизируя доступ к карте. Это единственный способ гарантировать, что счетчик не изменится из-за изменений, внесенных в другой поток.

Синхронизировать доступ к карте с помощью synchronized или Semaphore, чтобы только один поток мог изменять элементы карты и счета в то время.

Использование дополнительного поля в качестве счетчика не гарантирует безопасность потока здесь, так как после изменения карты и перед обработкой счетчика другой поток может фактически изменить карту, а значение счетчика недействительно.

Именно по этой причине карта не сохраняет свой внутренний размер, но должна обходить элементы - чтобы дать наиболее точные результаты в данный момент времени.

EDIT: Чтобы быть 100% ясно, что это наиболее удобный способ достичь этого:

synchronized(yourMap){ 
    doSomethingWithTheMap(); 
    yourMap.size(); 
} 

так что если вы будете менять каждую операцию по карте для такого блока, вы будете гарантировать, что size() будет вернуть точное количество элементов. Единственное условие - все манипуляции с данными выполняются с использованием такого синхронизированного блока.

+0

BTW, notThreadSafe HashMap отслеживает размер внутри, сохраняя его в поле. Итак, в некотором смысле, единственный способ - применить шаблон монитора и синхронизировать все, не так ли? Таким образом, нет необходимости в CoincurrentMap и счетчике размеров. HashMap будет работать хорошо. –

+0

Вот почему это NTS :) Если вы будете использовать блок «synchronized» для доступа к карте и количеству отсчетов, то да, это будет TS, и счет будет точным. Но если вы только синхронизируете методы манипуляции с картами и вызываете 'getSize()' вне синхронизированной области, это все равно будет NTS и не будет гарантировано быть последовательным. – Antoniossss

+0

Хорошо, получилось, спасибо большое. Но надежно ли рассчитывать на то, что HashMap не будет выполнять обход при получении размера? Или было бы безопаснее отслеживать размер самостоятельно, выбирая его в поле? –

Смежные вопросы