这是一个班级:
public class Refreshable<T> {
private final Object lock = new Object();
private final Supplier<Object>[] dynamicSettings;
private final Supplier<T> constructor;
private volatile Object[] instanceKey;
private volatile T instance;
@SafeVarargs
public Refreshable(Supplier<T> constructor, Supplier<Object>... dynamicSettings) {
this.constructor = constructor;
this.dynamicSettings = dynamicSettings;
}
public T getInstance() {
Object[] currentKey = getCurrentKey();
if (!Arrays.equals(currentKey, instanceKey)) {
synchronized (lock) {
if (!Arrays.equals(currentKey, instanceKey)) {
instanceKey = currentKey;
instance = constructor.get();
}
}
}
return instance;
}
private Object[] getCurrentKey() {
return Arrays.stream(dynamicSettings)
.map(Supplier::get)
.toArray();
}
}
这个想法在名称中:当与其创建相关的参数值发生变化时返回新实例。 constructor
始终是包装构造函数的lambda - 它不能返回null。但它可以抛出异常
在我们的项目中使用如下:
new Refreshable<Service>(
() -> new Service(settings.getParam1(), settings.getParam2()),
settings::getParam1,
settings::getParam1);
settings
返回的参数值可能随时间而变化。
长话短说,在一些负载测试下getInstance()
在第一次调用时返回多个线程(在同一秒内有10个或更多个线程),返回如下所示的行:serviceRefreshable.getInstance().someMethod()
。所以它是可刷新的null,或getInstance()的结果,以及错误在运行的后期没有重复的事实表明后者,因为refreshable只分配一次(它是一个单例bean)。
我理解constructor
可以抛出异常并阻止分配instance
,但它不在日志中(而NPE是)。
Arrays.equals()
此处无法意外返回true,因为instanceKey
最初为null且getCurrentKey()
无法返回null。所有dynamicSettings
个供应商都是包含此调用的lambda:
private <T> T getValue(String key, Class<T> type) {
Object value = properties.get(key);
if (value == null) {
throw new ConfigurationException("No such setting: " + key);
}
if (!type.isInstance(value)) {
throw new ConfigurationException("Wrong setting type" + key);
}
return (T) value;
}
这怎么可能?
我也理解通过将instance和instanceKy包装在一个对象中并将2个赋值减少到一个以使它们成为原子,以及检查instance == null
以及比较数组,可以防止这种情况发生。
我不明白的是,导致constructor
失败的异常导致地狱去了:)。
我甚至编辑了生产代码,在提供给其中一个constructor
的{{1}} lambda中抛出RuntimeException。果然,我在日志中看到了异常,而不是NPE。没有任何类型的并发负载,但