簡體   English   中英

以下實用程序類是否是線程安全的?

[英]Is the following utility class thread-safe?

首先讓我們看看實用程序類(大多數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
    }
}

最后,有以下輔助Spring托管bean實際調用'setContext'方法:

public final class ApplicationContextProviderBean implements
    ApplicationContextAware {

    public void setApplicationContext(
        final ApplicationContext applicationContext) throws BeansException {
    ApplicationContextUtils.setContext(applicationContext);
    }
}

應用程序啟動后,Spring將調用setApplicationContext方法一次。 假設nincompoop以前沒有調用ApplicationContextUtils.setContext(),那應該鎖定對實用程序類中上下文的引用,允許調用getContext()成功(意味着isInitialized()返回true)。

我只是想知道這個類是否違反了良好編碼實踐的任何原則,特別是線程安全(但是其他的愚蠢行為是受歡迎的)。

感謝您幫助我成為更好的程序員,StackOverflow!

此致,LES

PS我沒有說明為什么我需要這個實用程序類 - 讓我確實有合法的需要從應用程序中的任何地方的靜態上下文中訪問它(當然,在加載Spring上下文之后)。

不,這不是線程安全的。

對於通過getContext()讀取該變量的線程,不保證寫入context類變量。

至少,聲明contextvolatile 理想情況下,將context重新定義為AtomicReference ,通過以下調用進行設置:

if(!context.compareAndSet(null, theContext))
  throw new IllegalStateException("The context is already set.");

這是一個更完整的例子:

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();
  }

}

請注意,除了線程安全之外,這還提供了類型安全性,利用了傳遞給getBean()方法的Class實例。

我不確定你打算如何使用isInitialized()方法; 這對我來說似乎沒什么用處,因為一旦你打電話給它,情況就會發生變化,你就沒有好的方法得到通知。

Spring已經有一個名為ContextSingletonBeanFactoryLocator的類,它為您連接到ApplicationContext靜態訪問。 至少,使用這個類可能會省去你不必擔心自定義方法是否是線程安全的麻煩。

然而,最初使用這個類有點令人困惑,因為有一點間接性。 您可以查看此博客文章 ,了解有關此調用如何工作的更多信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM