Như Daniel đã viết, một phần của sự cố khi cố gắng viết theo kiểu Java với Double xuất phát từ thực tế là Double
trong Scala là scala.Double
chứ không phải java.lang.Double
. Nếu bạn muốn chương trình theo kiểu Java bạn sẽ phải đi dọc theo dòng sau đây:
//
// 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 chứa một wrapper Scala cho ConcurrentMap
s, vì vậy bạn có thể dễ dàng tránh được những vấn đề của java.lang.Double
vs scala.Double
. Tôi sẽ giữ cấu trúc chương trình trong giây lát.
//
// 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)
Trong biến b) trên đây có không phải là vấn đề đại diện cho các giá trị bị mất tích như 0.0d cũng không phải là vấn đề mà java.lang.Double
không chơi độc đáo với các nhà khai thác và boxing. Nhưng cả hai phiên bản a) và b) đều có vấn đề về hành vi tương tranh của chúng. Mã của Mansoor sử dụng ConcurrentHashMap
, có mục đích cho phép truy cập đồng thời vào bản đồ. Trong phiên bản gốc của mã, có khả năng một bản cập nhật cho bản đồ bị mất giữa việc lấy value
cũ và lưu trữ value + time
. Biến thể c) bên dưới cố gắng tránh sự cố đó.
//
// 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)
Cảm ơn bạn đã giải thích –