Ste*_* B. 32 java synchronization
可能重复:
增加java中锁的数量的最佳方法是什么
假设我想基于整数id值进行锁定.在这种情况下,有一个函数可以从缓存中提取值,并且如果值不存在则执行相当昂贵的检索/存储到缓存中.
现有代码未同步,可能会触发多个检索/存储操作:
//psuedocode
public Page getPage (Integer id){
Page p = cache.get(id);
if (p==null)
{
p=getFromDataBase(id);
cache.store(p);
}
}
Run Code Online (Sandbox Code Playgroud)
我想要做的是同步id上的检索,例如
if (p==null)
{
synchronized (id)
{
..retrieve, store
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不起作用,因为2个单独的调用可以具有相同的Integer id值但是具有不同的Integer对象,因此它们不会共享锁,并且不会发生同步.
有没有一种简单的方法可以确保您拥有相同的Integer实例?例如,这会工作:
syncrhonized (Integer.valueOf(id.intValue())){
Run Code Online (Sandbox Code Playgroud)
Integer.valueOf()的javadoc似乎意味着您可能会获得相同的实例,但这看起来不像是一个保证:
返回表示指定int值的Integer实例.如果不需要新的Integer实例,通常应优先使用此方法,而不是构造函数Integer(int),因为此方法可能通过缓存频繁请求的值来显着提高空间和时间性能.
那么,关于如何获得保证相同的Integer实例的任何建议,除了更精细的解决方案,比如保持Lock对象的WeakHashMap键入int?(没错,看起来似乎必须有一个明显的单行而不是我错过了).
Edd*_*die 50
你真的不想同步Integer,因为你无法控制哪些实例是相同的,哪些实例是不同的.Java只是不提供这样的工具(除非你在小范围内使用整数),这些工具可以在不同的JVM之间可靠.如果你真的必须在整数上进行同步,那么你需要保留一个Map或整数集,以便保证你得到你想要的确切实例.
更好的方法是创建一个新对象,可能存储在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);
}
Run Code Online (Sandbox Code Playgroud)
为了解释这个代码,它使用了ConcurrentMap允许使用的代码putIfAbsent.你可以这样做:
locks.putIfAbsent(id, new Object());
Run Code Online (Sandbox Code Playgroud)
但是你会因为每次访问而产生(小)成本.为了避免这种情况,我只是将整数本身保存在Map.这实现了什么?为什么这与使用Integer本身有什么不同?
get()从a 执行a 时Map,会将键与之进行比较equals()(或者至少使用的方法相当于使用equals()).具有相同值的两个不同的Integer实例将彼此相等.因此,您可以将任意数量的不同Integer实例new Integer(5)作为参数传递给,getCacheSyncObject并且您将始终只返回包含该值的第一个实例.
有些原因可能导致您不希望同步Integer...如果多个线程在Integer对象上进行同步,并且因此在他们想要使用不同的锁时无意中使用相同的锁,则可能会陷入死锁.您可以使用以下方法来解决此风险
locks.putIfAbsent(id, new Object());
Run Code Online (Sandbox Code Playgroud)
版本,因此每次访问缓存都会产生(非常)小的成本.这样做,您可以保证此类将在其他类不同步的对象上进行同步.总是一件好事.
使用线程安全映射,例如ConcurrentHashMap. 这将允许您安全地操作地图,但使用不同的锁来进行真正的计算。通过这种方式,您可以使用单个映射同时运行多个计算。
使用ConcurrentMap.putIfAbsent,但不要放置实际值,而是使用Future计算量较小的结构。可能是FutureTask实施。运行计算,然后运行get结果,这将线程安全地阻塞直到完成。