Безопасны ли коллекции цепочек с резьбой? - PullRequest
1 голос
/ 27 января 2011

Если у меня есть следующая декларация:

Map<String, Map<String, Person>> families = 
   Collections.synchronizedMap(new HashMap<String, Map<String, Person>>());

Если я тогда приковываю вызов так:

families.get(lastName).put(firstName, new Person());

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

EDIT Отличные ответы, сделанные в обоих ответах, спасибо большое! Но теперь я думаю, что если бы я сделал это:

families.put(lastName, Collections.synchronizedMap(new HashMap<String, Person>());

затем сделал мой цепной вызов, безопасна ли эта цепная нить? Возможно ли, что между get(lastName) и put(firstName, new Person()) что другой поток может получить внутреннюю карту? Я думаю, что если я хочу, чтобы весь цепной поток был безопасным, мне нужно поместить его в синхронизированный блок, но мне также интересно, будет ли это работать также ...

Ответы [ 3 ]

4 голосов
/ 27 января 2011

Внутренняя карта не поточно-ориентированная.

Если какой-то другой поток делает

families.get(lastName).put(firstName, new Person());

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

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

4 голосов
/ 27 января 2011

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

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

1 голос
/ 27 января 2011

Вполне возможно, что между вызовами внешнего get () и внутреннего put () какой-то другой поток также вызывает внешний get () и получает ту же внутреннюю карту. Но поскольку он синхронизирован, он все равно должен быть безопасным.

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

if (!families.containsKey(lastName)) {
  families.put(lastName, Collections.synchronizedMap(new HashMap<String, Person>());
}

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

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

...