Как писал Даниэль, часть проблемы при попытке написать в стиле Java с Double возникает из-за того, что Double
в Scala - это scala.Double
, а не java.lang.Double
.Если вы хотите программировать в стиле Java, вам нужно будет выполнить следующие действия:
//
// a) Java style, with concurrency problem
//
import java.lang.{Double=>JDouble}
import java.util.concurrent.ConcurrentHashMap
val map = new ConcurrentHashMap[String, JDouble]
def update(name: String, time: Double) {
val value: JDouble = map.get(name)
if (value eq null)
map.put(name, time)
else
map.put(name, JDouble.valueOf(value.doubleValue + time))
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map.get("foo") == 83.0d)
Scala 2.8 содержит оболочку Scala для ConcurrentMap
s, поэтому вы легко можете избежать проблемы java.lang.Double
против scala.Double
.Я сохраню структуру программы на мгновение.
//
// b) Scala style, with concurrency problem.
//
import collection.JavaConversions._
import collection.mutable.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
val map: ConcurrentMap[String, Double] = new ConcurrentHashMap[String,Double]()
def update(name: String, time: Double) {
map.get(name) match {
case None => map.put(name, time)
case Some(value) => map.put(name, value + time)
}
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map("foo") == 83.0d)
В варианте b) выше нет ни проблемы с представлением отсутствующего значения как 0.0d, ни проблемы с тем, что java.lang.Double
не работает красивос операторами и боксом.Но обе версии a) и b) сомнительны относительно их поведения параллелизма.Код Мансура использует ConcurrentHashMap
, цель которого - одновременный доступ к карте.В исходной версии кода существует вероятность потери обновления карты между получением старого value
и сохранением value + time
.Вариант с) ниже пытается избежать этой проблемы.
//
// c) Scala style, hopefully safe for concurrent use ;)
//
import collection.JavaConversions._
import collection.mutable.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
val map: ConcurrentMap[String, Double] = new ConcurrentHashMap[String,Double]()
def update(name: String, time: Double) {
val valueOption: Option[Double] = map.putIfAbsent(name, time)
def replace(value: Double) {
val replaced = map.replace(name, value, value + time)
if (!replaced) {
replace(map(name))
}
}
valueOption foreach { replace(_) }
}
update("foo", 42.0d)
update("foo", 41.0d)
assert(map("foo") == 83.0d)