Подтвердить что ты не робот

Объяснить синхронизацию коллекций при использовании итераторов?

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

Hashtable<Integer,Integer> map = new Hashtable<Integer,Integer>();

void dosomething1(){
    for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
        // do something
    }
}
void dosomething2(){
    for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
        // do something
        // and remove it
        i.remove();
    }
}
void putsomething(int a, int b){
    map.put(a,b);
}
void removesomething(int a){
    map.remove(a);
}
var clear(){
    map = new Hashtable<Integer,Integer>();
}

Может ли кто-нибудь объяснить, есть ли какие-то подводные камни, когда я вызываю эти функции наугад из разных потоков? Как итератор, в частности, выполняет свою синхронизацию, особенно когда он использует entrySet(), который, как представляется, также требует синхронизации? Что произойдет, если clear() вызывается, когда выполняется одна из циклов? Что делать, если removeesomething() удаляет элемент, который еще не обработан параллельным циклом в dosomething1()?

Спасибо за любую помощь!

4b9b3361

Ответ 1

Итерация по коллекциям в Java не является потокобезопасной, даже если вы используете одну из синхронизированных оболочек (Collections.synchronizedMap(...)):

Обязательно, чтобы пользователь вручную синхронизировал map при повторении любого из его видов коллекции:

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());
}

Документы Java Framework Framework

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

public int size() {
    synchronized( this ) {
        return collection.size();
    }
}

с collection, являющимся исходной коллекцией. Это работает для всех методов, открытых коллекцией/картой, за исключением итерации.

Ключ-набор карт синхронизируется точно так же: синхронизированная оболочка не возвращает исходный набор ключей вообще. Вместо этого он возвращает специальную синхронизированную оболочку набора исходных ключей коллекции. То же самое относится к набору записей и установленному значению.