在整数值上同步

2022-09-01 05:04:56

可能的重复:
在java中增加锁数量的最佳方法是什么

假设我想根据整数id值进行锁定。在本例中,有一个函数从缓存中提取值,如果值不存在,则执行相当昂贵的检索/存储到缓存中。

现有代码未同步,可能会触发多个检索/存储操作:

//psuedocode
public Page getPage (Integer id){
   Page p = cache.get(id);
   if (p==null)
   {
      p=getFromDataBase(id);
      cache.store(p);
   }
}

我想做的是同步id上的检索,例如

   if (p==null)
   {
       synchronized (id)
       {
        ..retrieve, store
       }
   }

不幸的是,这不起作用,因为 2 个单独的调用可以具有相同的 Integer id 值,但具有不同的 Integer 对象,因此它们不会共享锁,也不会发生同步。

有没有一种简单的方法来确保您具有相同的整数实例?例如,这是否有效:

 syncrhonized (Integer.valueOf(id.intValue())){

Integer.valueOf() 的 javadoc 似乎暗示你可能会得到相同的实例,但这看起来并不像是一个保证:

返回一个 Integer 实例,该实例表示指定的 int 值。如果不需要新的 Integer 实例,则通常应优先使用此方法而不是构造函数 Integer(int),因为此方法可能会通过缓存频繁请求的值来显著提高空间和时间性能。

那么,关于如何获得一个保证相同的整数实例的任何建议,除了更复杂的解决方案,如保持锁对象的WeakHashMap键控到int?(这没什么错,似乎一定有一个明显的单行线,而不是我错过的)。


答案 1

您真的不想在 上同步,因为您无法控制哪些实例是相同的,哪些实例是不同的。Java只是没有提供这样一个工具(除非你在小范围内使用整数),这种工具在不同的JVM上是可靠的。如果您确实必须在整数上进行同步,则需要保留映射或整数集,以便保证获得所需的确切实例。Integer

最好是创建一个新对象,可能存储在由 键控的 中以进行同步。像这样:HashMapInteger

public Page getPage(Integer id) {
  Page p = cache.get(id);
  if (p == null) {
    synchronized (getCacheSyncObject(id)) {
      p = getFromDataBase(id);
      cache.store(p);
    }
  }
}

private ConcurrentMap<Integer, Integer> locks = new ConcurrentHashMap<Integer, Integer>();

private Object getCacheSyncObject(final Integer id) {
  locks.putIfAbsent(id, id);
  return locks.get(id);
}

为了解释此代码,它使用 ,它允许使用 。你可以这样做:ConcurrentMapputIfAbsent

  locks.putIfAbsent(id, new Object());

但是,您会产生为每个访问创建对象的(小)成本。为了避免这种情况,我只是将整数本身保存在.这实现了什么?为什么这与仅使用整数本身有什么不同?Map

当您从 a 执行 a 时,将键与 (或至少使用的方法等效于 using ) 进行比较。同一值的两个不同的 Integer 实例将彼此相等。因此,您可以将任意数量的 “” 的不同 Integer 实例作为参数传递给 ,并且您将始终仅返回包含该值的传入的第一个实例。get()Mapequals()equals()new Integer(5)getCacheSyncObject

您可能不想同步的原因...如果多个线程在对象上同步,并且当他们想要使用不同的锁时,它们会不知不觉地使用相同的锁,则可能会陷入死锁。您可以通过使用IntegerInteger

  locks.putIfAbsent(id, new Object());

版本,因此每次访问缓存都会产生(非常)小的成本。这样,就可以保证此类将在没有其他类将同步的对象上执行同步。总是一件好事。


答案 2

使用线程安全映射,例如 。这将允许您安全地操作地图,但使用不同的锁来执行实际计算。通过这种方式,您可以使用单个地图同时运行多个计算。ConcurrentHashMap

使用 ,但不是放置实际值,而是使用 具有计算轻量级结构的 。可能是实现。运行计算,然后运行结果,这将线程安全地阻塞,直到完成。ConcurrentMap.putIfAbsentFutureFutureTaskget


推荐