Ответ
Вы можете проверить его опытным путем, создав множество потоков, которые пытаются добавить / удалить те же элементы из общего mutable.Set
import java.util.concurrent.{Callable, ExecutorService, Executors}
import scala.collection.mutable
def testSet(set: mutable.Set[Int]) {
val e: ExecutorService = Executors.newFixedThreadPool(5)
val tasks = for (i <- 0 to 50000) yield {
e.submit(new Callable[Unit]() {
override def call() {
for (j <- 0 to 10) {
set.add(i + j)
// This will throw a Null Pointer Exception for the non-concurrent version
// This is because it is trying to remove something that another thread already removed.
set.remove(i + j)
}
}
})
}
for (result <- tasks) result.get()
e.shutdown()
}
// Can't check the type! They have the same type.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala
testSet(concurrentSet) // Success!
testSet(regularSet) // FAILS! Almost always throws a NullPointerException on my system.
Ограничения
Выполнение теста потребует системных ресурсов, таких как потоки и время процессора. На самом деле нецелесообразно запускать это в рабочем режиме во время выполнения.
Это не дедуктивное доказательство . Существует небольшая вероятность того, что тест классифицирует не одновременный объект как параллельный, поскольку условие гонки является случайным. Однако выполнение теста дольше приведет к тому, что вероятность обнаружения несовпадающих объектов приблизится к достоверности.
Дополнительные комментарии
В идеале был бы способ использовать отражение и систему типов, чтобы увидеть, что лежал в основе объекта, и проверить, является ли он ConcurrentHashMap
или нет (по моему мнению, это серьезный недостаток в Scala, поскольку некоторые функции, выполняющие несколько многопоточная задача не может эффективно предотвратить передачу функции вызывающей функции в непараллельный объект.
Но это, по крайней мере, дает эмпирический способ проверить это.
Подтверждения
Аналогичный вопрос был задан в Как проверить, что ConcurrentHashMap действительно поточно-ориентирован? . Я изменил его для работы с наборами:
Рекомендация
Я бы порекомендовал использовать concurrent.Map[T, Unit]
вместо mutable.Set[T]
. Причина в том, что вы сможете использовать систему типов, чтобы с 100% -ной уверенностью гарантировать, что объект, над которым работает ваша функция / класс, действительно параллелен.
Да, вы потеряете семантику Set, такую как метод .add(setMember)
. Но вы получите безопасность.
Если вы настаиваете на использовании одновременного mutable.Set
, рассмотрите возможность создания контейнера класса-обертки, чтобы вы могли предотвратить случайную инициализацию для не одновременного mutable.Set
.