Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在整数值上同步_Java_Synchronization - Fatal编程技术网

Java 在整数值上同步

Java 在整数值上同步,java,synchronization,Java,Synchronization,可能重复: 假设我想要基于整型id值进行锁定。在这种情况下,有一个函数可以从缓存中提取一个值,如果该值不在缓存中,则执行相当昂贵的检索/存储操作 现有代码未同步,可能会触发多个检索/存储操作: //psuedocode public Page getPage (Integer id){ Page p = cache.get(id); if (p==null) { p=getFromDataBase(id); cache.store(p); } }

可能重复:

假设我想要基于整型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
       }
   }
不幸的是,这不起作用,因为两个单独的调用可以具有相同的整数id值,但具有不同的整数对象,因此它们不会共享锁,也不会发生同步

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

 syncrhonized (Integer.valueOf(id.intValue())){
Integer.valueOf()的javadoc似乎暗示您可能会得到相同的实例,但这并不能保证:

返回一个整数实例 表示指定的int值。 如果未创建新的整数实例 如果需要,通常应使用此方法 优先于 构造函数整数(int),如下所示 方法很可能会产生 明显更好的空间和时间 通过频繁缓存提高性能 请求的值

那么,除了将锁对象的WeakHashMap设置为int键等更复杂的解决方案外,还有什么建议可以保证得到一个相同的整数实例吗?(这没什么问题,只是看起来肯定有一行代码比我缺少的更明显)。

Integer.valueOf()
只返回有限范围内的缓存实例。您尚未指定范围,但通常情况下,这不起作用

但是,我强烈建议您不要采用这种方法,即使您的值在正确的范围内。由于这些缓存的
Integer
实例可用于任何代码,因此无法完全控制同步,这可能导致死锁。这与人们试图锁定
String.intern()
的结果时遇到的问题相同

最好的锁是一个私有变量。因为只有您的代码可以引用它,所以您可以保证不会发生死锁


顺便说一下,使用
WeakHashMap
也不起作用。如果用作密钥的实例未被引用,则它将被垃圾收集。如果它是强引用的,您可以直接使用它。

您确实不想在
整数上同步,因为您无法控制哪些实例相同,哪些实例不同。Java只是没有提供跨不同JVM的可靠功能(除非您使用的是小范围的整数)。如果您确实必须对整数进行同步,那么您需要保留一个映射或一组整数,这样您就可以保证获得所需的确切实例

最好是创建一个新对象,可能存储在由
整数
键控的
HashMap
中,以便同步。大概是这样的:

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);
}
但是,为每次访问创建一个对象会产生(很小的)成本。为了避免这种情况,我只将整数本身保存在
映射中。这能实现什么?为什么这与仅仅使用整数本身有什么不同

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

您可能不想在
Integer
上进行同步,这是有原因的。。。如果多个线程在
Integer
对象上同步,并且在想要使用不同锁时无意中使用了相同的锁,则可能会陷入死锁。您可以通过使用

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

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

用整数对象作为键的ConcurrentHashMap怎么样?

您可以看看如何从ID创建互斥体。代码是为字符串ID编写的,但可以很容易地为整数对象进行编辑。

在整数上使用synchronized听起来设计上确实错误

如果仅在检索/存储期间需要单独同步每个项目,则可以创建一个集合并将当前锁定的项目存储在该集合中。换句话说,

// this contains only those IDs that are currently locked, that is, this
// will contain only very few IDs most of the time
Set<Integer> activeIds = ...

Object retrieve(Integer id) {
    // acquire "lock" on item #id
    synchronized(activeIds) {
        while(activeIds.contains(id)) {
            try { 
                activeIds.wait();   
            } catch(InterruptedExcption e){...}
        }
        activeIds.add(id);
    }
    try {

        // do the retrieve here...

        return value;

    } finally {
        // release lock on item #id
        synchronized(activeIds) { 
            activeIds.remove(id); 
            activeIds.notifyAll(); 
        }   
    }   
}
//仅包含当前已锁定的ID,即
//大多数情况下只包含很少的ID
设置ActiveID=。。。
对象检索(整数id){
//获取项目#id的“锁”
已同步(ActiveID){
while(activeIds.contains(id)){
试试{
activeIds.wait();
}catch(interruptedexception e){…}
}
add(id);
}
试一试{
//在这里进行检索。。。
返回值;
}最后{
//解除对项目#id的锁定
已同步(ActiveID){
activeid.remove(id);
activeIds.notifyAll();
}   
}   
}
商店也是如此


底线是:没有一行代码可以完全按照您需要的方式解决此问题。

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

使用
ConcurrentMap.putIfAbsent
,但不要使用plac
// this contains only those IDs that are currently locked, that is, this
// will contain only very few IDs most of the time
Set<Integer> activeIds = ...

Object retrieve(Integer id) {
    // acquire "lock" on item #id
    synchronized(activeIds) {
        while(activeIds.contains(id)) {
            try { 
                activeIds.wait();   
            } catch(InterruptedExcption e){...}
        }
        activeIds.add(id);
    }
    try {

        // do the retrieve here...

        return value;

    } finally {
        // release lock on item #id
        synchronized(activeIds) { 
            activeIds.remove(id); 
            activeIds.notifyAll(); 
        }   
    }   
}
ConcurrentMap<Integer, java.util.concurrent.Future<Page>> cache = new ConcurrentHashMap<Integer, java.util.concurrent.Future<Page>>;

public Page getPage(Integer id) {
    Future<Page> myFuture = new Future<Page>();
    cache.putIfAbsent(id, myFuture);
    Future<Page> actualFuture = cache.get(id);
    if ( actualFuture == myFuture ) {
        // I am the first w00t!
        Page page = getFromDataBase(id);
        myFuture.set(page);
    }
    return actualFuture.get();
}