gpt4 book ai didi

java - 单向同步: how to block on one particular method?

转载 作者:行者123 更新时间:2023-11-30 05:04:33 26 4
gpt4 key购买 nike

大家好,SO 用户。

我目前正在编写一个类,该类的实例将用作 JavaBean PropertyDescriptors 的缓存。您可以调用方法getPropertyDescriptor(Class clazz, String propertyName),该方法将返回适当的PropertyDescriptor。如果之前没有检索到,则会获取该类的 BeanInfo 实例并找到正确的描述符。然后将该结果存储为类名对,以便下次可以立即返回它,而无需查找或需要 BeanInfo

首先要考虑的是同一类的多次调用何时会重叠。通过同步 clazz 参数可以简单地解决这个问题。因此,对同一类的多个调用是同步的,但对不同类的调用可以不受阻碍地继续。这似乎是线程安全性和活跃性之间的一个不错的折衷方案。

现在,在某些时候可能需要卸载某些已内省(introspection)的类。我不能简单地保留对它们的引用,因为这可能会导致类加载器泄漏。此外,JavaBeans API 的 Introspector 类提到类加载器销毁应与内省(introspection)器的刷新相结合:http://download.oracle.com/javase/6/docs/api/java/beans/Introspector.html

因此,我添加了一个方法 flushDirectory(ClassLoader cl) ,它将从缓存中删除任何类并将其从 Introspector 中刷新(使用 Introspector.flushFromCaches(Class clz)) 前提是它是用该类加载器加载的。

现在我对同步有一个新的担忧。当刷新正在进行时,不应将新映射添加到缓存中,而如果访问仍在进行,刷新不应启动。换句话说,基本问题是:

如何确保一段代码可以由多个线程运行,而另一段代码只能由一个线程运行并禁止其他代码段运行? 这是一种单向同步。

首先,我尝试了 java.util.concurrent.LockAtomicInteger 的组合来记录正在进行的调用数量,但注意到一个锁只能获取,不能检查当前是否正在使用,不加锁。现在,我在原子整数上的对象上使用简单的同步。这是我的类(class)的精简版本:

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class DescriptorDirectory {

private final ClassPropertyDirectory classPropertyDirectory = new ClassPropertyDirectory();
private final Object flushingLock = new Object();
private final AtomicInteger accessors = new AtomicInteger(0);

public DescriptorDirectory() {}

public PropertyDescriptor getPropertyDescriptor(final Class<?> clazz, final String propertyName) throws Exception {

//First incrementing the accessor count.
synchronized(flushingLock) {
accessors.incrementAndGet();
}

PropertyDescriptor result;

//Synchronizing on the directory Class root
//This is preferrable to a full method synchronization since two lookups for
//different classes can never be on the same directory path and won't collide
synchronized(clazz) {

result = classPropertyDirectory.getPropertyDescriptor(clazz, propertyName);

if(result == null) {
//PropertyDescriptor wasn't loaded yet

//First we need bean information regarding the parent class
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(clazz);
} catch(final IntrospectionException e) {
accessors.decrementAndGet();
throw e;
//TODO: throw specific
}

//Now we must find the PropertyDescriptor of our target property
final PropertyDescriptor[] propList = beanInfo.getPropertyDescriptors();
for (int i = 0; (i < propList.length) && (result == null); i++) {
final PropertyDescriptor propDesc = propList[i];
if(propDesc.getName().equals(propertyName))
result = propDesc;
}

//If no descriptor was found, something's wrong with the name or access
if(result == null) {
accessors.decrementAndGet();
//TODO: throw specific
throw new Exception("No property with name \"" + propertyName + "\" could be found in class " + clazz.getName());
}

//Adding mapping
classPropertyDirectory.addMapping(clazz, propertyName, result);

}

}

accessors.decrementAndGet();

return result;

}

public void flushDirectory(final ClassLoader cl) {

//We wait until all getPropertyDescriptor() calls in progress have completed.
synchronized(flushingLock) {

while(accessors.intValue() > 0) {
try {
Thread.sleep(100);
} catch(final InterruptedException e) {
//No show stopper
}
}

for(final Iterator<Class<?>> it =
classPropertyDirectory.classMap.keySet().iterator(); it.hasNext();) {
final Class<?> clazz = it.next();
if(clazz.getClassLoader().equals(cl)) {
it.remove();
Introspector.flushFromCaches(clazz);
}
}

}

}

//The rest of the inner classes are omitted...

}

我相信这应该有效。假设线程1调用get...方法,线程2同时调用flush...方法。如果线程 1 首先获得 flushingLock 上的锁,线程 2 将等待访问器计数返回到 0。与此同时,对 get... 的新调用无法继续,因为线程 2 现在将有flushingLock。如果线程 2 首先获得锁,它将等待访问器下降到 0,而调用 get... 将等待刷新完成。

任何人都可以看到这种方法的问题吗?是否有一些我忽略的场景?或者也许我把事情过于复杂化了。最重要的是,一些 java.util.concurrent 类可能完全提供了我在这里所做的事情,或者有一个标准模式可以应用于我不知道的这个问题。

抱歉这篇文章太长了。它并没有那么复杂,但仍然远非简单,所以我认为关于正确方法的一些讨论会很有趣。

感谢所有阅读本文并提前提供答案的人。

最佳答案

据我了解,您可以使用 ReadWriteLock 这里:

private ReadWriteLock lock = new ReentrantReadWriteLock();
private Lock readLock = lock.readLock();
private Lock writeLock = lock.writeLock();

public PropertyDescriptor getPropertyDescriptor(final Class<?> clazz, final String propertyName) throws Exception {
readLock.lock();
try {
...
} finally {
readLock.unlock();
}
}

public void flushDirectory(final ClassLoader cl) {
writeLock.lock();
try {
...
} finally {
writeLock.unlock();
}
}

也在 Class 上同步实例对我来说看起来很可疑 - 它可能会干扰其他一些同步。也许使用线程安全的 Map 会更好的Future<PropertyDescriptor> (例如,参见Synchronization in a HashMap cache)。

关于java - 单向同步: how to block on one particular method?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5550666/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com