确保非最终领域的“单身式”独特性,在哪里锁定?

时间:2012-06-09 19:23:14

标签: java multithreading locking singleton synchronized

我希望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”的点上,如果我们想要,只是为了参数的缘故,如何最好地检查连接是否仍然有效并重新创建它,如果它不是? / p>

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

编辑:

我想让jdbcConnection做的是:

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

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

(例如,客户端1出现,获取连接,但关闭它,客户端2出现,连接不为空但不能使用,所以她得到一个再生的。)

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

4 个答案:

答案 0 :(得分:0)

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

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

return jdbcConnection!=NULL?jdbcConnection:jdbcConnection();

答案 1 :(得分:0)

双重检查锁定应该在不在this上的类上完成:

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

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

答案 2 :(得分:0)

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

避免任何同步问题的最佳方法是初始化单行内联或静态初始化器,因为初始化发生在首次访问类时,并且保证初始化不会被并发性破坏,即在完全初始化之前,不能使用该类:

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方法才会发生。再一次,由于类初始化以线程安全的方式发生,因此可以确保单例的创建不会受到损害。

答案 3 :(得分:0)

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

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连接的想法感兴趣。