Синхронизация вложенных структур данных между потоками в Java - PullRequest
0 голосов
/ 09 февраля 2011

У меня есть реализация кеша:

class X
{
  private final Map<String, ConcurrentMap<String, String>> structure = new HashMap...(); 

  public String getValue(String context, String id)
  {
     // just assume for this example that there will be always an innner map
     final ConcurrentMap<String, String> innerStructure = structure.get(context);

     String value = innerStructure.get(id);
     if(value == null)
     {
       synchronized(structure)
       {
          // can I be sure, that this inner map will represent the last updated
          // state from any thread?
          value = innerStructure.get(id);
          if(value == null)
          {
            value = getValueFromSomeSlowSource(id);
            innerStructure.put(id, value); 
          }
       }
     }       
     return value;
  }
}

Является ли эта реализация поточно-ориентированной?Могу ли я быть уверен, что последнее состояние обновлено из какого-либо потока внутри синхронизированного блока?Изменится ли это поведение, если я использую java.util.concurrent.ReentrantLock вместо синхронизированного блока, например:

...
if(lock.tryLock(3, SECONDS))
{
  try
  {
    value = innerStructure.get(id);
    if(value == null)
    {
      value = getValueFromSomeSlowSource(id);
      innerStructure.put(id, value); 
    }
  }
  finally
  {
    lock.unlock();
  }
}
...

Я знаю, что конечные элементы экземпляра синхронизируются между потоками, но верно ли этообъекты, которыми владеют эти члены?

Может быть, это глупый вопрос, но я не знаю, как это проверить, чтобы убедиться, что он работает на каждой ОС и каждой архитектуре.

Ответы [ 3 ]

1 голос
/ 09 февраля 2011

В случае реализации класса X значение innerStructure может быть обновлено только тогда, когда значение не является нулевым.Таким образом, предполагается, что все значение innerStructure не является нулевым.В этом предположении, если структура и innerStructure не обновляются без getValue (), это потокобезопасно.

Если структура и innerStructure обновляются в другой функции, значение, которое возвращает getValue, может нестановится самым последним значением.

Хотя кажется, что повышение производительности является целью, проблема и обходные пути описаны на следующем сайте.

http://www.ibm.com/developerworks/java/library/j-jtp03304/

(Устраняет ли это проблему блокировки с двойной проверкой)

1 голос
/ 09 февраля 2011

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

В вашей программе, в указанном контексте, да, вы можете предположить, что String вы получаетеэто самая последняя версияОднако ваш код все еще небезопасен, потому что вы читаете значение из Map за пределами блока synchronized.Если это чтение происходит в то же время, когда в Map вставлено значение, вы не гарантированно получите разумное значение.Я знаю, что по крайней мере в некоторых реализациях это может вызвать бесконечный цикл из-за некоторой странности в реализации.

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

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

0 голосов
/ 09 февраля 2011

Возможно, вам понадобится использовать ConcurrentHashMap для внешнего Hashmap и InnerHashMap для синхронизированных операций get и put на уровне Bucket

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...