[英]maven java.lang.IllegalAccessError: tried to access method com.google.common.collect.MapMaker.makeComputingMap
[英]Using MapMaker#makeComputingMap to prevent simultaneous RPCs for the same data
我们有一个速度较慢的后端服务器,该服务器受到负载的挤压,我们希望中间层Scala服务器对于每个唯一的查询仅对后端有一个未完成的请求。
后端服务器仅存储不可变的数据,但是在添加新数据后,中间层服务器将代表客户端请求最新数据,并且后端服务器难以承受负载。 使用写入时生成的唯一键将不可变数据缓存在memcached中,但是写入速率很高,因此我们得到的memcached命中率较低。
我有一个想法是使用Google Guava的MapMaker#makeComputingMap()包装实际的查找,并在ConcurrentMap#get()返回之后,中间层将保存结果,只是从Map中删除键。
尽管代码很容易编写,但这似乎有点浪费,请参见下面的示例说明。
是否有更自然的数据结构,库或番石榴的一部分可以解决此问题?
import com.google.common.collect.MapMaker
object Test
{
val computer: com.google.common.base.Function[Int,Long] =
{
new com.google.common.base.Function[Int,Long] {
override
def apply(i: Int): Long =
{
val l = System.currentTimeMillis + i
System.err.println("For " + i + " returning " + l)
Thread.sleep(2000)
l
}
}
}
val map =
{
new MapMaker().makeComputingMap[Int,Long](computer)
}
def get(k: Int): Long =
{
val l = map.get(k)
map.remove(k)
l
}
def main(args: Array[String]): Unit =
{
val t1 = new Thread() {
override def run(): Unit =
{
System.err.println(get(123))
}
}
val t2 = new Thread() {
override def run(): Unit =
{
System.err.println(get(123))
}
}
t1.start()
t2.start()
t1.join()
t2.join()
System.err.println(get(123))
}
}
我不确定为什么要实施删除自己的工作,为什么不简单地具有弱或软值并让GC为您清理呢?
new MapMaker().weakValues().makeComputingMap[Int, Long](computer)
我认为您的做法很合理。 您仅使用该结构来获取密钥上的锁条,以确保对同一密钥的访问冲突。 不用担心您不需要每个键的值映射。 ConcurrentHashMap
和friends是Java库+ Guava中唯一为您提供锁条的结构。
这确实会导致一些次要的运行时开销,以及您不需要的哈希表的大小(如果对同一段的访问堆积并且remove()无法跟上的话,哈希表的大小甚至可能会增加)。
如果您想使其尽可能便宜,则可以自己编写一些简单的带锁代码。 基本上是N个锁(N =并发级别)的Object[]
(或Array[AnyRef]
:)),您只需将查找键的哈希映射到该数组中并进行锁定。 这样做的另一个优点是,您实际上不必执行CHM所需的哈希码技巧,因为后者必须将哈希码分为一个部分来选择锁,而另一部分则用于哈希表的需要,但是您可以将其全部用于锁定选择。
编辑 :在下面草绘我的评论:
val concurrencyLevel = 16
val locks = (for (i <- 0 to concurrencyLevel) yield new AnyRef).toArray
def access(key: K): V = {
val lock = locks(key.hashCode % locks.size)
lock synchronized {
val valueFromCache = cache.lookup(key)
valueFromCache match {
case Some(v) => return v
case None =>
val valueFromBackend = backendServer.lookup(key)
cache.put(key, valueFromBackend)
return valueFromBackend
}
}
}
(顺便说一句,是否需要toArray
调用?或者返回的IndexSeq已经可以通过索引快速访问了?)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.