Ответ 1
Из документа: "Если несколько потоков обращаются к этой таблице одновременно, и один из потоков изменяет таблицу, ее необходимо синхронизировать извне".
Совпадающих коллекций сборок недостаточно.
Будет ли Guava метод tables.newCustomTable(Map, Supplier) возвращает потокобезопасные таблицы при поставке с помощью безопасных по потоку карт? Например:
public static <R, C, V> Table<R, C, V> newConcurrentTable() {
return Tables.newCustomTable(
new ConcurrentHashMap<R, Map<C, V>>(),
new Supplier<Map<C, V>>() {
public Map<C, V> get() {
return new ConcurrentHashMap<C, V>();
}
});
}
Действительно ли этот код возвращает параллельные таблицы?
Из документа: "Если несколько потоков обращаются к этой таблице одновременно, и один из потоков изменяет таблицу, ее необходимо синхронизировать извне".
Совпадающих коллекций сборок недостаточно.
Кевин Бурриллион прав. Техническая причина для карты, которую вы создали, чтобы не быть потокобезопасной, заключается в том, что даже если используемые вами карты являются потокобезопасными, операции с таблицей могут быть неактивными. Позвольте мне привести пример put, реализованный в StandardTable
, который используется Tables.newCustomTable
:
public V put(R rowKey, C columnKey, V value) {
Map<C, V> map = backingMap.get(rowKey);
if (map == null) {
map = factory.get();
backingMap.put(rowKey, map);
}
return map.put(columnKey, value);
}
Безопасность при обращении с корпусом map == null
скомпрометирована. А именно, два или более потока могут войти в этот блок и создать новую запись для columnKey
, а последняя для выполнения backingMap.put(rowKey, map)
в конечном итоге переопределит запись для columnKey
в backingMap
, что приведет к потеря операций put
, выполняемых другими потоками. В частности, результат этой операции в многопоточной среде не является детерминированным, что эквивалентно утверждению, что эта операция не является потокобезопасной.
Правильная реализация этого метода:
public V put(R rowKey, C columnKey, V value) {
ConcurrentMap<C, V> map = table.get(rowKey);
if (map == null) {
backingMap.putIfAbsent(rowKey, factory.get());
}
map = backingMap.get(rowKey);
return map.put(columnKey, value);
}
В настоящее время я изучаю, можно ли использовать реализацию ForwardingTable
вместе с тем, что вы хотели сделать, чтобы получить корректный поток ConcurrentTable
.
Но, честно говоря, я думаю, что причина отсутствия поточной реализации Table
заключается в том, что сам интерфейс не предоставляет никаких конструкций concurrency, таких как putIfAbsent
или replace
.