0

在我们的一个遗留应用程序中有一个数据库连接泄漏,我追踪到这个小宝石。从调试,我可以看到多个线程返回相同的逻辑连接(不好!)。但我正在努力理解为什么会这样。发现并发问题

我们使用ojdbc6驱动程序,使用连接池在WebLogic数据源上进行设置。产生问题

public class MyDummyDaoUtil { 

    //note: this is a public field in a singleton (not a static field though...) 
    public Connection conn; 

    private MyDummyDaoUtil() { 
    } 

    public static MyDummyDaoUtil getInstance() { 
     if (instance == null) { 
      instance = new MyDummyDaoUtil(); 
     } 

     return instance; 
    } 

    private DataSource getDataSource(final String dsName) 
     throws NamingException { 
     return ServiceLocator.getInstance().getDataSource(dsName); 
    } 

    public static Connection getConnection(final String source) 
     throws NamingException { 
     return MyDummyDaoUtil.getInstance().getDBConnection(source); 
    } 

    private Connection getDBConnection(final String source) 
     throws NamingException { 

     //the same logical connection is produced by the data source or something else happening? 
     conn = getDataSource(source).getConnection(); 

     conn.setAutoCommit(false); 

     return conn; 
    } 
} 

更新修复

public class MyDummyDaoUtil { 

    private MyDummyDaoUtil() { 
    } 

    public static MyDummyDaoUtil getInstance() { 
     if (instance == null) { 
      instance = new MyDummyDaoUtil(); 
     } 

     return instance; 
    } 

    private DataSource getDataSource(final String dsName) 
     throws NamingException { 
     return ServiceLocator.getInstance().getDataSource(dsName); 
    } 

    public static Connection getConnection(final String source) 
     throws NamingException { 
     return MyDummyDaoUtil.getInstance().getDBConnection(source); 
    } 

    private Connection getDBConnection(final String source) 
     throws NamingException { 

     Connection conn = getDataSource(source).getConnection(); 
     conn.setAutoCommit(false); 

     return conn; 
    } 
} 

摘要修复

  1. 不正确的懒惰initializa的

    代码实例的重刑

  2. 连接应该是方法的局部变量,而不是单例类
+0

你说MyDummyDaoUtil是一个单身人士。如果这是在多个线程之间共享的,那么将有两个连接被请求的机会,这两个连接被请求变异引用和两次调用getDBConnection返回的相同引用(最后一个)。不能返回的连接永远不能关闭。这段代码没有结束耗尽可用连接? – Yoztastic 2014-10-08 17:57:07

+0

@Yoztastic当他们被垃圾收集时,连接可能被他们的终结者击落,隐藏了这个问题。 – Durandal 2014-10-08 19:28:08

+0

@Yoztastic - 是的,那是最初的症状。我们的连接已经耗尽,我们不得不打开无效超时来帮助回收这些泄露的连接,同时我们追查问题。从我所知道的情况来看,代码是从第1天开始实施的(这很吓人......)目前还不清楚这个问题没有在我们的旧系统上显示出来。我的猜测是连接被以某种方式回收,并且我们没有意识到存在这个问题。 – user1766760 2014-10-10 16:19:21

回答

0

您正在使用Singleton模式,你必须处理在同步方式的对象实例中多使用时螺纹环境。

尝试任何一个选项:

  • 使用getInstance()同步方法

    public static synchronized MyDummyDaoUtil getInstance() { 
        if (instance == null) { 
         instance = new MyDummyDaoUtil(); 
        } 
        return instance; 
    } 
    
  • 初始化它热切

    private static MyDummyDaoUtil instance = new MyDummyDaoUtil(); 
    public static MyDummyDaoUtil getInstance() { 
        return instance; 
    } 
    
  • 使用DOUB乐检查锁定机制

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

注:不要忘记关闭一个它完成连接。

Read more...

+0

好点,我错过了实例的不正确的懒惰初始化 – user1766760 2014-10-10 16:45:26

0

假设你所要求的是“为什么更改此代码修复getDBConnection的返回上的多个线程对同一对象的问题” ......

你有可变状态( MyDummyDaoUtil.conn)您正在使用。请考虑以下情形 - 两个线程(A和B)都呼吁在同一时间你原来的功能:

private Connection getDBConnection(final String source) 
    throws NamingException { 
    conn = getDataSource(source).getConnection(); //line 1 
    conn.setAutoCommit(false);     //line 2 
    return conn;         //line 3 
} 

这里有很多可能的序列,但这里有一个样品有问题之一:

  • 线程A执行第1行。您的数据源会返回一个新连接(我们将其称为connectionA),并且MyDummyDaoUtil.conn设置为connectionA
  • 线程B执行第1行。您的数据源返回一个新连接(我们称之为connectionB),MyDummyDaoUtil.conn设置为connectionB
  • 线程A执行第2行。conn现在是connectionB,所以这会导致在connectionB上将自动提交设置为false。
  • 线程A执行第3行,返回connectionB(这是从另一个线程创建的线程)。
  • 线程B执行2号线,设置自动提交为false connectionB(这是一个空操作,因为线程A已经做到了事故)
  • 线程B执行3号线,返回connectionB

问题是,MyDummyDaoUtil.conn,因为它是一个单身成员变量,在两个线程中引用相同的变量。在第二个示例中,有一个局部变量的事实意味着每次调用该函数都有一个单独的变量,因此您不会在调用之间发生交叉污染。