2012-05-19 4 views
4

От javadocsCollections.synchronizedMap

Map m = Collections.synchronizedMap(new HashMap()); 
     ... 
    Set s = m.keySet(); // Needn't be in synchronized block 
     ... 
    synchronized(m) { // Synchronizing on m, not s! 
     Iterator i = s.iterator(); // Must be in synchronized block 
     while (i.hasNext()) 
      foo(i.next()); 
    } 

двух запросов:

1) устанавливается возвращаемый m.keySet() также коллекция обертка или просто синхронизирован набор?

EDIT:

2) Нужно ли синхронизировать м в

synchronized(m) { // Synchronizing on m, not s! 

Мы не можем синхронизировать с вместо т?

ответ

5

1: Да, он возвращает синхронизированный набор, который разделяет мьютекс с Картой.

2: Да, вам нужно удерживать замок вручную во время итерации. Если вы не можете внести изменения между вызовами на следующий(), и у вас все еще будут проблемы. Помните, что это часть спецификации HashMap, что если другой поток, например, делает m.put("foo", "bar"); между двумя вашими вызовами до i.next(), то next() будет вызывать ConcurrentModificationException. Чтобы этого не произошло, вы блокируете всю карту, поэтому никто не может ее изменить, пока вы не закончите с итератором. Блокировка только набора не помешала бы кому-либо добавить к карте.

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

+0

для (1) означает, что установленный набор будет работать так же, как Collections.synchronizedSet (...). Для (2) см. Править. –

+2

Нет, это не так, как если бы вы получили его из synchronizedSet, потому что когда он синхронизирует его, он блокирует родительскую карту, а не сам по себе. – Affe

3

1) Возвращенный набор - это SynchronizedSet, который использует один и тот же мьютекс для блокировки.

2) Карта не синхронизируется сама по себе, а является отдельным объектом мьютекса, поэтому синхронизация на карте не остановит другие потоки, у которых есть доступ к карте от ее изменения. Это будет работать, только если все кода, который изменил карту, также синхронизирован на карте. Карта действительно синхронизируется сама по себе, но странно относится к ней, используя отдельную переменную mutex. Поэтому синхронизация на карте будет препятствовать другим изменениям карты через синхронизированную оболочку. Обратите внимание, что один и тот же мьютекс используется в синнизированном наборе, возвращаемом методом keySet().

+0

Убирайтесь из моего ума! .... – Affe

+0

Ха-ха, очень похожий ответ, вы получили ссылку на «ConcurrentMap», хотя, так что +1 к вам. Также стоит упомянуть Guava 'LoadingCache', который улучшается на' ConcurrentMap': http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/LoadingCache.html – SimonC

+0

Конечно, мы взяли время, чтобы прочитать * весь * источник, мы бы заметили, что JDK устанавливает mutex = this; упс! – Affe

1

EDITED: Неправильно раньше.

  1. Это SynchronizedSet, синхронизированный непосредственно на экземпляре SynchronizedMap.
  2. Необходимо синхронизировать на m, чтобы убедиться, что карта не изменяется из-под вас, когда вы находитесь в цикле.

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

Поучительно посмотреть на code. В строке 1999 есть объявление SynchronizedMap.

+1

@SimonC Я прикрепил ссылку на код - внутренний объект mutex является экземпляром 'SynchronizedMap'. Таким образом, блокировка на 'm' означает, что никто не сможет ее изменить, поскольку все операции модификации блокируют внутренний мьютекс, который является' m'. – Chip

+0

@PauKiatWee Исправлено. – Chip

+0

Справедливая точка зрения, я удалил свой нисходящий и неправильный комментарий. – SimonC

0

Просто уточняйте ответ с помощью некоторого кода источника.

http://www.docjar.com/html/api/java/util/Collections.java.html

1) Да, это коллекция обертка (экземпляр частного статического класса SynchronizedSet), как указано в line 2054 из Collections кода класса.

2051  public Set<K> keySet() { 
2052    synchronized (mutex) { 
2053     if (keySet==null) 
2054      keySet = new SynchronizedSet<>(m.keySet(), mutex); 
2055     return keySet; 
2056    } 
2057   } 

Обратите внимание, что этот SynchronizedSet использует тот же семафор, который SynchronizedMap, возвращенное Collections.synchronizedMap(new HashMap()); использует.

2) Необходимо синхронизировать его на m вместо s, потому что все операции в возвращаемом наборе (SynchronizedSet) в Set s = m.keySet(); синхронизируются на одном и том же мьютексе (возвращена Синхронизированная карта), но не на возвращенном наборе. Это можно увидеть из следующих точек:

a) Все операции в Map (SynchronizedMap), возвращаемые Collections.synchronizedMap(new HashMap());, синхронизируются по возвращенной карте iteslf как мьютексу, как видно из следующих строк кода, как видно из строк 2004,2010,2035. :

1992  public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { 
1993   return new SynchronizedMap<>(m); 
1994  } 

SynchronizedMap класс определяется как:

1999  private static class SynchronizedMap<K,V> 
2000   implements Map<K,V>, Serializable { 
2001   ... 
2002 
2003   private final Map<K,V> m;  // Backing Map 
2004   final Object  mutex;  // Object on which to synchronize 
       ... 
       SynchronizedMap(Map<K,V> m) { 
2007    if (m==null) 
2008     throw new NullPointerException(); 
2009    this.m= m; 
2010    mutex = this; 
2011   } 
       ... 
2034   public V put(K key, V value) { 
2035    synchronized (mutex) {return m.put(key, value);} 
2036   } 
       ... 
      } 

б) когда мы итерация карты, Iterator i = s.iterator(); мы должны синхронизировать его на т вместо й, так как операции в возвращаемой Set (SynchronizedSet) в Set s = m.keySet(); синхронизируются же мьютекса (возвращается Синхронное карте), но не на с, как показано в строке 2054.

2051  public Set<K> keySet() { 
2052    synchronized (mutex) { 
2053     if (keySet==null) 
2054      keySet = new SynchronizedSet<>(m.keySet(), mutex); 
2055     return keySet; 
2056    } 
2057   } 
Смежные вопросы