繁体   English   中英

仅具有内部状态的静态成员的类

[英]Class with only static members which has an internal state

我感觉好像我的设计还没到位,我陷入了两种技术之间。 我正在尝试编写一个类,分发与数据库的连接。 代码如下:

public final class DBUtil {

  private static String databaseDriver = null;
  private static String databaseConnectionString = null;
  private static String databaseUser = null;
  private static String databasePassword = null;
  private static String serverName = null;
  private static ComboPooledDataSource dataSource = null;

  private DBUtil() {
    throw new AssertionError();
  }

  private static void getParameters() {
    final Properties configFile = new Properties();
    try {
      configFile.load(DBUtil.class.getClassLoader().getResourceAsStream("my.properties"));
      if (configFile.containsKey("databaseConnectionString") && configFile.containsKey("databaseUser") && configFile.containsKey("databasePassword") && configFile.containsKey("databaseDriver")) {
        DBUtil.databaseConnectionString = configFile.getProperty("databaseConnectionString");
        DBUtil.databaseDriver = configFile.getProperty("databaseDriver");
        DBUtil.databaseUser = configFile.getProperty("databaseUser");
        DBUtil.databasePassword = configFile.getProperty("databasePassword");
      }
      else {
        // Properties file not configured correctly for database connection
      }
    }
    catch (IOException e) {}
  }

  public static Connection getDatabaseConnection() {
    if (Strings.isNullOrEmpty(databaseConnectionString) || Strings.isNullOrEmpty(databaseUser) || Strings.isNullOrEmpty(databasePassword) || Strings.isNullOrEmpty(databaseDriver)) {
      DBUtil.getParameters();
     }
    dataSource = getDataSource();
    int retryCount = 0;
    Connection connection = null;
    while (connection == null) {
      try {
        connection = dataSource.getConnection();
      }
      catch (SQLException sqle) {}
    }
    return connection;
  }

  private static ComboPooledDataSource getDataSource() {
    if (dataSource == null) {
      dataSource = new ComboPooledDataSource();
      try {
        dataSource.setDriverClass(databaseDriver);
        dataSource.setJdbcUrl(databaseConnectionString);
        dataSource.setUser(databaseUser);
        dataSource.setPassword(databasePassword);
      }
      catch (PropertyVetoException pve) {}
    }
    return dataSource;
  }

  public static void cleanUpDataSource() {
    try {
      DataSources.destroy(dataSource);
    }
    catch (SQLException sqle) {}
  }
}

当我这样做时,FindBugs正在重新启动Incorrect lazy initialization and update of static field

if (dataSource == null) {
      dataSource = new ComboPooledDataSource();
      ...

任何建议,不胜感激。 我感觉好像卡在Singleton模式和一个仅由一组静态方法组成的类之间的某个地方。

更一般而言,这是将数据库连接分发给DAO的好方法吗?

非常感谢。

为了避免两次并行执行(两个线程同时调用它),应该synchronized该方法。

添加

未同步的异常可能导致两个线程执行此操作:

T1   if (datasource == null) YES
T2                               if (datasource == null) YES
T1   datasource = new Datasource...
T2                               datasource = new Datasource(...); AGAIN!

和两个数据源之一上的T1和T2调用方法(取决于T2何时覆盖T1对象创建)。

挥发物

正如@ Jean-Marc建议的那样,您应该将datasource字段声明为volatile 该关键字确保线程不使用变量的线程本地副本(如果线程读取过期的缓存值,则可能导致问题)。

我不确定这种情况是否在不同的方法调用之间发生,或者是否syncrhonized处理,但是最好确定:)

该代码的问题在于,在存在多头处理的情况下,您最终可能会创建ComboPooledDataSource多个实例, ComboPooledDataSource dataSource在不同的时间指向不同的实例。

如果该方法大约同时被多个线程调用,则可能发生这种情况。

假设dataSourcenull ,并且两个线程的执行交织如下:

Thread 1: if (dataSource == null) { // condition is true
Thread 2: if (dataSource == null) { // condition is true
Thread 1: dataSource = new ComboPooledDataSource();
Thread 2: dataSource = new ComboPooledDataSource();

解决并发问题的一种简单方法是添加同步。

Findbugs抱怨因为您的getDataSource方法未同步。 使用当前代码,两个并发线程可以调用getDataSource并检索到单独的DataSource对象。

这就是为什么FindBugs抱怨的原因:

http://findbugs.sourceforge.net/bugDescriptions.html#LI_LAZY_INIT_UPDATE_STATIC

如前所述,如果您没有多个线程,则应该没有问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM