Чем Copy-On-Write отличается от прямой блокировки / синхронизации при записи? - PullRequest
3 голосов
/ 09 июня 2019

Копирование при записи считается одной из лучших практик в сценариях параллелизма. Тем не менее, я не понимаю, как это отличается от простой блокировки / синхронизации в методе записи. Может ли кто-нибудь помочь объяснить это?

Copy-On-Write:

    public V put(K key, V value) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            V val = newMap.put(key, value);
            internalMap = newMap;
            return val;
        }
    }

Прямая блокировка / синхронизация:

    public V put(K key, V value) {
        synchronized (this) {
            internalMap.put(key, value);
        }
    }

Для потоков записи они взаимно исключены в приведенных выше 2 примерах, то же самое.

Для потоков чтения, в Copy-On-Write, действия чтения после выполнения «internalMap = newMap» получат обновленное значение. А в прямой блокировке действия чтения после запуска «internalMap.put (key, value)» получат обновленное значение, примерно то же самое.

Так почему же мы продвигаем Copy-On-Write? Почему мы должны «копировать» при записи?

Ответы [ 2 ]

5 голосов
/ 10 июня 2019

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

2 голосов
/ 10 июня 2019

Как при использовании блокировки, так и при копировании при записи достигается (практически) одинаковая функциональность. Ни один из них по своей природе не лучше другого.

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

Почему записи дороже, вероятно, очевидно (вам нужно копировать всю карту при каждой записи, да). Причина чтения дешевле:

volatile Map<K, V> internalMap = new HashMap<>();

Чтение InternalMap не требует получения блокировки (подробнее см. Разница между volatile и синхронизированными в Java ). Как только потоки получили ссылку на internalMap, они могут просто продолжать работать с этой копией (например, перебирая записи), не координируя с другими потоками, потому что гарантируется, что она не будет видоизменена. Столько потоков, сколько необходимо, могут обработать одну копию (снимок) карты.

Чтобы объяснить по аналогии, представьте, что автор готовит статью, и у них есть несколько человек, работающих в качестве проверщиков фактов. С замком только один из них может работать на сквозняке. С копией при записи автор отправляет неизменный снимок (копию) куда-то, что контролеры фактов могут захватить и выполнить свою работу - пока они выполняют свою работу, они могут читать снимок по мере необходимости (вместо того, чтобы прерывать автора каждый раз, когда они забыл части статьи и т. д.).

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

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