java - 在整数值上同步

标签 java synchronization

Possible Duplicate:
What is the best way to increase number of locks in 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 对象,因此它们不会共享锁,并且不会发生同步。

是否有一种简单的方法可以确保您拥有相同的 Integer 实例?例如,这是否可行:

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

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

Returns a Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values.

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

最佳答案

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

更好的办法是创建一个新对象,可能存储在 HashMap 中,该对象由 Integer 键控,以进行同步。像这样的:

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);
}

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

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

但随后您会产生(少量)为每次访问创建对象的成本。为了避免这种情况,我只是将 Integer 本身保存在 Map 中。这能达到什么目的?为什么这与仅使用 Integer 本身有什么不同?

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

您可能不想在 Integer 上同步的原因...如果多个线程在 Integer 对象上同步并且因此无意中使用了,您可能会陷入死锁当他们想使用不同的锁时,相同的锁。您可以使用

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

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

关于java - 在整数值上同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/659915/

相关文章:

java - 有人可以教我如何将此段落条目写为 boolean 表达式吗?

android - 在 Android 应用程序中与服务器同步

java - DAO 方法和同步

java - 如何克隆 Java 中的异常

java - Java中具有数字名称的文件排序

java - 返回 2 个字符串的连接 java

java - 无法使用线程同步进程

java - Java 中用于线程间使用的互斥体?

java - elsif 与 nextboolean 用于图片

java - JUnit 忽略代码更改(也许使用已编译代码的缓存版本?)