我在Tomcat中使用JDBC连接池。要检索连接,我已经定义了一个连接工厂,如下所示:
public class ConnectionManager {
// reference to the ConnectionManager
private static ConnectionManager instance = null;
// Connection to MySQL database
private Connection connect = null;
private static DataSource ds = null;
// Logger
public static final Logger logger = Logger
.getLogger(ConnectionManager.class);
static {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Private Constructor .. since its a singleton
*
*/
private ConnectionManager() {
}
public static ConnectionManager getInstance() {
if (instance == null) {
instance = new ConnectionManager();
}
return instance;
}
public Connection getDbConnection() {
Connection conn = null;
try {
synchronized (DataSource.class) {
conn = ds.getConnection();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public void closeDbConnection() throws SQLException {
conn.close();
}
}
现在我发现我的代码总是卡在conn = ds.getConnection();
行。请让我知道我做错了什么。从我的DAO方法中,我使用以下内容来获取连接:conn = ds.getConnection();
显然它是一个多线程问题。我该怎么办?
答案 0 :(得分:3)
您的大部分课程似乎都围绕检索JNDI数据源并使用它来创建连接。不一定是个糟糕的主意,但在这种情况下,您已经在程序中引入了一些错误,而且复杂性很高。
首先,你的单身人士不是单身人士。您没有同步getInstance
方法,因此可以在多个线程上同时调用此方法。 Java(不幸的是)用于实现单例的最佳方法是通过枚举:
public enum ConnectionManager {
INSTANCE;
}
您的第二个重要问题是您正在同步一个您不明确控制的课程。没有什么能阻止第三方JAR甚至您自己的应用程序中的其他类在DataSource类上进行同步,这使其成为死锁问题的普遍目标。我会从类中取出所有多余的方法并删除同步块:
public enum ConnectionManager {
INSTANCE;
private DataSource ds = null;
ConnectionManager() {
try {
final Context initCtx = new InitialContext();
final Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
} catch (NamingException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
if(ds == null) return null;
return ds.getConnection();
}
}
现在,根据我的经验,大多数数据源实现都是线程安全的,因此上述代码应该在大多数情况下都能正常工作。但是,我们不应该依赖我们无法控制的实现,所以让我们为代码添加安全同步,如下所示:
public enum ConnectionManager {
INSTANCE;
private DataSource ds = null;
private Lock connectionLock = new ReentrantLock();
ConnectionManager() {
try {
final Context initCtx = new InitialContext();
final Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
} catch (NamingException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
if(ds == null) return null;
Connection conn = null;
connectionLock.lock();
try {
conn = ds.getConnection();
} finally {
connectionLock.unlock();
}
return conn;
}
}
您不必添加包装器方法来关闭连接,这是调用代码的责任。祝你好运。
答案 1 :(得分:0)
答案 2 :(得分:0)
嗯,我会说先试用你的dataSource
是否正常使用测试源
我建议查看适用于Apache Tomcat 6.0和Apache Tomcat 7.0的Apache Tomcat JNDI数据资源的方法。
仔细查看说明并分析代码中出现的问题,然后针对特定问题更新问题。
答案 3 :(得分:0)
该代码几乎可以保证在多线程系统中导致连接泄漏。 closeDbConnection()
仅关闭从池中借用的最后一个连接 - 因此,如果10个线程调用了getDbConnection()
,那么closeDbConnection()
之后,只有1个连接被关闭,9个连接仍处于活动状态。重复几次并且池耗尽(除非在finalize()
中清除连接,但情况可能并非如此)。我会摆脱整个班级,或者将其重新设计为仅作为数据源定位器。