在整数值上同步 [英] Synchronizing on an Integer value

查看:20
本文介绍了在整数值上同步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

假设我想基于一个整数 id 值进行锁定.在这种情况下,有一个函数可以从缓存中提取一个值,并在缓存中不存在该值时进行相当昂贵的检索/存储.

Suppose I want to lock based on an integer id value. In this case, there's a function that pulls a value from a cache and does a fairly expensive retrieve/store into the cache if the value isn't there.

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

The existing code isn't synchronized and could potentially trigger multiple retrieve/store operations:

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

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

What I'd like to do is synchronize the retrieve on the id, e.g.

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

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

Unfortunately this won't work because 2 separate calls can have the same Integer id value but a different Integer object, so they won't share the lock, and no synchronization will happen.

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

Is there a simple way of insuring that you have the same Integer instance? For example, will this work:

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

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

The javadoc for Integer.valueOf() seems to imply that you're likely to get the same instance, but that doesn't look like a guarantee:

返回一个整数实例表示指定的 int 值.如果一个新的 Integer 实例不是需要,这种方法一般应该优先使用构造函数 Integer(int),如 this方法可能会产生明显更好的空间和时间通过频繁缓存来提高性能请求的值.

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?(这并没有错,只是似乎肯定有一个明显的单行比我错过了).

So, any suggestions on how to get an Integer instance that's guaranteed to be the same, other than the more elaborate solutions like keeping a WeakHashMap of Lock objects keyed to the int? (nothing wrong with that, it just seems like there must be an obvious one-liner than I'm missing).

推荐答案

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

You really don't want to synchronize on an Integer, since you don't have control over what instances are the same and what instances are different. Java just doesn't provide such a facility (unless you're using Integers in a small range) that is dependable across different JVMs. If you really must synchronize on an Integer, then you need to keep a Map or Set of Integer so you can guarantee that you're getting the exact instance you want.

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

Better would be to create a new object, perhaps stored in a HashMap that is keyed by the Integer, to synchronize on. Something like this:

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.你可以这样做:

To explain this code, it uses ConcurrentMap, which allows use of putIfAbsent. You could do this:

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

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

but then you incur the (small) cost of creating an Object for each access. To avoid that, I just save the Integer itself in the Map. What does this achieve? Why is this any different from just using the Integer itself?

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

When you do a get() from a Map, the keys are compared with equals() (or at least the method used is the equivalent of using equals()). Two different Integer instances of the same value will be equal to each other. Thus, you can pass any number of different Integer instances of "new Integer(5)" as the parameter to getCacheSyncObject and you will always get back only the very first instance that was passed in that contained that value.

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

There are reasons why you may not want to synchronize on Integer ... you can get into deadlocks if multiple threads are synchronizing on Integer objects and are thus unwittingly using the same locks when they want to use different locks. You can fix this risk by using the

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

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

version and thus incurring a (very) small cost to each access to the cache. Doing this, you guarantee that this class will be doing its synchronization on an object that no other class will be synchronizing on. Always a Good Thing.

这篇关于在整数值上同步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆