Scala: How to test the concurrency of a mutable.Set

我怕爱的太早我们不能终老 提交于 2019-12-11 00:34:02

问题


In Scala, both concurrent and non-concurrent Sets have exactly the same type:

// A regular Set in Scala, not concurrent.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()

// A concurrent set. It has the same type as a regular set, but underneath, it is actually concurrent. In my opinion, this is a flaw in the type system for Scala collections
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala

I'd like a way to actually test whether a set is concurrent or not.


回答1:


Answer

You can test it empirically by creating a lot of threads which are trying to add/remove the same elements from a shared 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.

Limitations

Running the test will require system resources such as threads and CPU time. It is not really appropriate to run this in production at runtime.

This is not a deductive proof. There is a tiny chance that the test will classify a non-concurrent object as being concurrent, since the race condition is random. However, running the test for longer will cause the probability of detecting non-concurrent objects to approach certainty.

Additional Comments

Ideally there would be a way to use reflection and the type system to see what the underlying object was and test if it was a ConcurrentHashMap or not (major flaw in Scala in my opinion, since some function that runs a multi-threaded task cannot effectively prevent the the function caller from passing in a non-concurrent object).

But this at least provides an empirical way to test it.

Acknowledgments

A similar question was asked in How can I test that ConcurrentHashMap is truly thread-safe? . I've modified it to work with Sets:

Recommendation

I would recommend using a concurrent.Map[T, Unit] rather than mutable.Set[T]. The reason is that you will be able to leverage the type system to ensure, with 100% confidence, that the object your function/class operates on is actually concurrent.

Yes, you will lose the Set semantics, such as the .add(setMember) method. But you will gain safety.

If you insist on using a concurrent mutable.Set, consider making a wrapper class container so that you can prevent accidental initialization to a non-concurrent mutable.Set.



来源:https://stackoverflow.com/questions/56013503/scala-how-to-test-the-concurrency-of-a-mutable-set

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!