1

我希望 JdbcPersistenceManager 始终只有一个 jdbcConnection 并通过以下方式对其进行实例化: JdbcConnectionManager.getJdbcConnection()

这是取自 (2004-11-01 | Head First Design Patterns | O'Reilly Media | 688p | by Elisabeth Freeman | ISBN-0596007124) 的简单模式,可能被误用、误解和不合适。

synchronized() 应该锁定“this”还是专门用于跟踪锁定的特定新(静态?)字段?我该怎么做?

public class JdbcPersistenceManager implements PersistenceManager {
  private volatile Connection jdbcConnection;
  /* ... */
  private Connection getJdbcConnection() throws JdbcConnectionFailureException {
    if (jdbcConnection == null) {
      synchronized (this) {
        if (jdbcConnection == null) {
          jdbcConnection =
              JdbcConnectionManager.getJdbcConnection(jdbcConnectionParameters);
        }
      }
    }
    // label:here
    return jdbcConnection;
  }
}

假设 jdbcConnection 的实例化,即使我们想要标记为“label:here”的点,只是为了论证,如何最好地检查连接是否仍然有效,如果不是则重新创建它?

ConnectionPooling 不是我想在这里做的。只有一个连接......如果它是“null”或“invalid”,则重新生成/重新打开它。

编辑:

我想要 getJdbcConnection 做的是:

1) 提供 jdbcConnections,确保在任何给定时间只存在其中一个(不应允许客户端保留对 2 个不同连接的引用)

2) 重新生成(即重新调用 JdbcConnectionManager.getJdbcConnection())“private volatile Connection jdbcConnection”字段,如果由于某种原因它被关闭了

(例如,客户端 1 出现,获得连接但关闭它,客户端 2 出现,连接不为空但无法使用,因此她获得了重新生成的连接)。

注意:我意识到没有什么可以阻止客户端 1 获得连接,而客户端 2 获得相同的连接,按照设计,并且在客户端 1 通过他的参考关闭它后仅一毫秒使用它......我不知道这是否可以解决有吗?

4

4 回答 4

0

是的, synchronized() 锁只是用于跟踪锁,因此当看到jdbcConnection=NULL时,没有两个线程一个接一个地实例化jdbcConnection

如果要检查该// label:here位置的连接完整性。您可以递归调用 getJdbcConnection() 方法。

return jdbcConnection!=NULL?jdbcConnection:jdbcConnection();
于 2012-06-09T19:34:15.443 回答
0

双重检查锁定应该在类上完成而不是this

if (jdbcConnection == null) {
  synchronized (JdbcPersistenceManager.class) {
    if (jdbcConnection == null) {
      jdbcConnection =
          JdbcConnectionManager.getJdbcConnection(jdbcConnectionParameters);
    }
  }
}

根据连接检查,您可以按照您的建议在创建后进行。我不会递归调用该方法,只是null实例Connection并尝试getJdbcConnection()再次调用。

于 2012-06-09T19:57:14.490 回答
0

如果要实现单例模式,那么您要确保给定类的单个实例,这就是为什么单例模式通常使用静态实例来实现的原因。

避免任何同步问题的最好方法是内联或在静态初始化器中初始化单例,因为初始化发生在第一次访问类时,并且保证此初始化不会受到并发性的影响,也就是说,类不能在完全初始化之前一直使用:

public class Singleton {
    private static final Singlenton instance = new Singleton();

    //private constructor here

    public static Singleton getIntance() { return instance; } 
}

现在,如果您想懒惰地初始化您的单例,那么您将不得不考虑多个线程可能同时尝试获取它的实例的可能性,在这种情况下,您将同步对您的类的访问:

public class Singleton {
    private static Singlenton instance;

    //private constructor here

    public static synchronized Singleton getIntance() { 
       if(instance == null) {
            intance = new Singleton();
       }
       return instance; 
    } 
}

有些人会建议减少同步代码的范围,这样也可以减少给定线程持有锁的时间。在这种情况下,您可以执行以下操作:

public class Singleton {
    private static Singlenton instance;

    //private constructor here

    public static Singleton getIntance() { 
       synchronized(Singleton.class) {
          if(instance == null) {
               instance = new Singleton();
          }
       }
       return instance; 
    } 
}

在最后一个示例中,一旦安全地分配了单例实例,您就可以同时返回它。

另一种延迟初始化 Singleton 的模式是使用静态内部类:

public class Singleton {

  //private constructor

   private static class SingletonHolder { 
       public static final Singleton instance = new Singleton();
  }

   public static Singleton getInstance() {
       return SingletonHolder.instance;
   }
}

这意味着在第一次访问内部类之前不会创建单例,在调用 getIntance 方法之前不会发生这种情况。再一次,由于类初始化以线程安全的方式发生,您可以确保单例的创建不会受到影响。

于 2012-06-09T20:37:57.773 回答
0

除了@edalorzo 的综合列表之外,还有一种技术,singleton使用enum. 这里有一个很好的例子,它可能看起来有点像这样。

public enum SingletonConnection {
  INSTANCE;
  // Not sure if this needs to be volatile.
  private volatile Connection jdbcConnection;

  private SingletonConnection() {
    jdbcConnection = JdbcConnectionManager.getJdbcConnection(jdbcConnectionParameters);
  }

  public Connection getConnection() {
    return jdbcConnection;
  }
}

// use it as ...
SingletonConnection.INSTANCE.getConnection ();

但是,您可能对使用ThreadLocal连接的想法感兴趣。

于 2012-06-09T21:46:31.217 回答