les*_*es2 3 java spring static multithreading thread-safety
首先让我们看看实用程序类(大多数javadoc已被删除,只是示例):
public class ApplicationContextUtils {
/**
* The application context; care should be taken to ensure that 1) this
* variable is assigned exactly once (in the
* {@link #setContext(ApplicationContext)} method, 2) the context is never
* reassigned to {@code null}, 3) access to the field is thread-safe (no race
* conditions can occur)
*/
private static ApplicationContext context = null;
public static ApplicationContext getContext() {
if (!isInitialized()) {
throw new IllegalStateException(
"Context not initialized yet! (Has the "
+ "ApplicationContextProviderBean definition been configured "
+ "properly and has the web application finished "
+ "loading before you invoked this method?)");
}
return context;
}
public static boolean isInitialized() {
return context == null;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(final String name, final Class<T> requiredType) {
if (requiredType == null) {
throw new IllegalArgumentException("requiredType is null");
}
return (T) getContext().getBean(name, requiredType);
}
static synchronized void setContext(final ApplicationContext theContext) {
if (theContext == null) {
throw new IllegalArgumentException("theContext is null");
}
if (context != null) {
throw new IllegalStateException(
"ApplicationContext already initialized: it cannot be done twice!");
}
context = theContext;
}
private ApplicationContextUtils() {
throw new AssertionError(); // NON-INSTANTIABLE UTILITY CLASS
}
}
Run Code Online (Sandbox Code Playgroud)
最后,有以下辅助Spring托管bean实际调用'setContext'方法:
public final class ApplicationContextProviderBean implements
ApplicationContextAware {
public void setApplicationContext(
final ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtils.setContext(applicationContext);
}
}
Run Code Online (Sandbox Code Playgroud)
应用程序启动后,Spring将调用setApplicationContext方法一次.假设nincompoop之前没有调用ApplicationContextUtils.setContext(),那应该锁定对实用程序类中上下文的引用,允许调用getContext()成功(意味着isInitialized()返回true).
我只是想知道这个类是否违反了良好编码实践的任何原则,特别是线程安全(但是其他的愚蠢行为是受欢迎的).
感谢您帮助我成为更好的程序员,StackOverflow!
此致,LES
PS我没有说明为什么我需要这个实用程序类 - 让我确实有合法的需要从应用程序中的任何地方的静态上下文中访问它(当然,在加载Spring上下文之后).
不,这不是线程安全的.
对于context通过读取该变量的线程,不保证对类变量的写入可见getContext().
至少,宣布context为volatile.理想情况下,重新定义context为AtomicReference,通过这样的调用设置:
if(!context.compareAndSet(null, theContext))
throw new IllegalStateException("The context is already set.");
Run Code Online (Sandbox Code Playgroud)
这是一个更完整的例子:
public class ApplicationContextUtils {
private static final AtomicReference<ApplicationContext> context =
new AtomicReference<ApplicationContext>();
public static ApplicationContext getContext() {
ApplicationContext ctx = context.get();
if (ctx == null)
throw new IllegalStateException();
return ctx;
}
public static boolean isInitialized() {
return context.get() == null;
}
static void setContext(final ApplicationContext ctx) {
if (ctx == null)
throw new IllegalArgumentException();
if (!context.compareAndSet(null, ctx))
throw new IllegalStateException();
}
public static <T> T getBean(final String name, final Class<T> type) {
if (type == null)
throw new IllegalArgumentException();
return type.cast(getContext().getBean(name, type));
}
private ApplicationContextUtils() {
throw new AssertionError();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,除了线程安全性之外,这还提供了类型安全性,利用了Class传递给getBean()方法的实例.
我不确定你打算如何使用这种isInitialized()方法; 这对我来说似乎没什么用处,因为一旦你打电话给它,情况就会发生变化,你就没有好的方法得到通知.
| 归档时间: |
|
| 查看次数: |
2149 次 |
| 最近记录: |