上一篇构建的数据库连接池,今天拿来在项目中测试时,发现在处理高并发的问题上有很明显的缺点。
现在回过头来看我们上一篇的代码:
public class ConnectionPool {
/*线程安全数组*/
private volatile Vector<Connection> pool;
private volatile Connection selectConnection;
/*公有属性*/
private String url = "jdbc:mysql://127.0.0.1/text";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
/*连接池中储存的最大数量*/
private final int poolSize = 10;
/*当线程池耗尽时,最大新建数量*/
private final int newPoolSize = 10;
private volatile static int tab = 0;
private static ConnectionPool instance = null;
private Connection conn = null;
private static ConnectionPool mConnectionPool = null;
public static ConnectionPool getInstance() throws SQLException{
synchronized (Connection.class) {
if (mConnectionPool == null){
mConnectionPool = new ConnectionPool();
return mConnectionPool;
}
return mConnectionPool;
}
}
/*构造方法,做一些初始化工作*/
private ConnectionPool() throws SQLException {
pool = new Vector<Connection>(poolSize);
selectConnection = DriverManager.getConnection(url, username, password);
try {
for (int i = 0; i < poolSize; i++) {
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/*得到select使用的连接,查询不同于其他三项,查询可以并发进行执行,
* 所以我们只需提供一个连接来供所有用户使用*/
public Connection getSelectConnection() throws SQLException{
if(selectConnection != null){
return selectConnection;
}
synchronized (ConnectionPool.class) {
if(selectConnection == null){
selectConnection = DriverManager.getConnection(url, username, password);
}
}
return selectConnection;
}
/* 返回连接到连接池
* 在这里进行控制,如果连接池里的连接数大于我们规定的数量,则对此连接进行关闭
*/
public synchronized void release(Connection con) throws SQLException {
if(con == null){
return;
}
if (pool.size() >= poolSize){
tab-=1;
con.close();
return;
}
pool.add(con);
}
/* 返回连接池中的一个数据库连接
* 如果连接池中已经耗尽了Connextion
* 则创建新的使用
*/
public Connection getConnection() {
synchronized(ConnectionPool.class){
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
System.out.println("得到连接");
return conn;
}
if(tab < newPoolSize){
System.out.println("得到新建的连接");
return newConnection();
}
}
/*如果连接池耗尽,并且新建连接也到最大值,那么在这里排队等待*/
synchronized (ConnectionPool.class) {
try {
while(pool.size() == 0){
Thread.sleep(1*50);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Connection conn = pool.get(0);
pool.remove(conn);
System.out.println("得到回收之后重新利用的连接");
return conn;
}
}
/*新建连接*/
private Connection newConnection(){
try {
tab+=1;
Connection conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
return conn;
} catch (SQLException e) {
return null;
}
}
}
问题一:我们在release方法和getConnection方法都使用了synchronized,而且我们使用的都是一个对象ConnectionPool.class,意思就是说,当getConnection的对象锁被一个用户拿到,导致其他的用户无法执行release方法!
问题二:当第一个线程进入getConnection方法或者release方法,后面的用户就必须排队,所以在这方面我们需要进行优化,其实这个问题解决的空间还是很大的。
建议:
1、尽量使用tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
2、尽量使用java.util.concurrent包的并发类代替手写控制并发,比较常用的是ConcurrentHashMap、ConcurrentLinkedQueue、AtomicBoolean等等,实际应用中java.util.concurrent.atomic十分有用,简单方便且效率比使用Lock更高 。
3、尽量不要几个功能用同一把锁 。
4、尽量减少同步的代码块。
ok,下面是我优化过得代码,使用ReentrantLock替换掉了一部分synchronized,确保发生死锁时会自动跳出,不至于程序的崩溃。
public class ConnectionPool {
/*线程安全数组*/
private volatile Vector<Connection> pool;
private volatile Vector<Connection> recordPool;
private volatile Connection selectConnection;
/*公有属性*/
private String url = "jdbc:mysql://127.0.0.1/one?useSSL=true";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
/*连接池中储存的最大数量*/
private final int poolSize = 10;
/*当线程池耗尽时,最大新建数量*/
private final int newPoolSize = 80;
private static ConnectionPool instance = null;
private Connection conn = null;
private static ConnectionPool mConnectionPool = null;
/**
* 这里定义三组顶层父类,没一个父类对象控制一把锁,他们意义在与多个锁的互不排斥。
*/
private Object obj3 = new Object();
private Object obj4 = new Object();
private Object obj5 = new Object();
/*不同于synchronized的另一种锁机制*/
private Lock lock = new ReentrantLock();
private Lock lock2 = new ReentrantLock();
private Lock lock3 = new ReentrantLock();
private Lock lock4 = new ReentrantLock();
public static ConnectionPool getInstance() throws SQLException, ClassNotFoundException{
if(mConnectionPool != null){
return mConnectionPool;
}
synchronized (ConnectionPool.class) {
if (mConnectionPool == null){
mConnectionPool = new ConnectionPool();
}
return mConnectionPool;
}
}
/*构造方法,做一些初始化工作*/
private ConnectionPool() throws SQLException, ClassNotFoundException {
System.out.println("初始化开始");
pool = new Vector<Connection>();
recordPool = new Vector<Connection>();
selectConnection = load();
for (int i = 0; i < poolSize; i++) {
conn = load();
pool.add(conn);
}
System.out.println("初始化结束");
}
/*得到select使用的连接,查询不同于其他三项,查询可以并发进行执行,
* 所以我们只需提供一个连接来供所有用户使用*/
public Connection getSelectConnection() throws SQLException, InterruptedException{
if(selectConnection != null && !selectConnection.isClosed()){
return selectConnection;
}
if(lock2.tryLock(8L,TimeUnit.SECONDS)){
if(selectConnection == null || selectConnection.isClosed()){
selectConnection = load();
}
lock2.unlock();
}
return selectConnection;
}
/* 返回连接到连接池
* 在这里进行控制,如果连接池里的连接数大于我们规定的数量,则对此连接进行关闭
*/
public void release(Connection con) throws SQLException, InterruptedException {
if(con == null || con.isClosed()){
recordPool.remove(con);
return;
}
if(lock3.tryLock(8L,TimeUnit.SECONDS)){
if (pool.size() >= poolSize){
con.close();
lock3.unlock();
return;
}
if(pool.size() < poolSize){
pool.add(con);
}
lock3.unlock();
}
}
private Connection getNewConnection(){
/*考虑到多线程访问,防止当多名用户同时拿连接或回收时产生误差,在这里进行双层判断*/
if(recordPool.size() < newPoolSize){
if(pool.size() > 0){
return pool.remove(0);
}
if(recordPool.size() < newPoolSize){
Connection conn = load();
recordPool.add(conn);
System.out.println("得到新建的连接");
return conn;
}
}
return null;
}
/* 返回连接池中的一个数据库连接
* 如果连接池中已经耗尽了Connextion
* 则创建新的使用
*/
public Connection getConnection() throws ClassNotFoundException, InterruptedException {
if (pool.size() > 0) {
synchronized (obj3) {
if(pool.size() > 0){
System.out.println("得到连接");
return pool.remove(0);
}
}
}
if(lock4.tryLock(8L,TimeUnit.SECONDS)){
Connection con = getNewConnection();
lock4.unlock();
if(con != null){
return con;
}
}
/*如果连接池耗尽,并且新建连接也到最大值,那么在这里排队等待*/
try {
synchronized (obj5) {
while(true){
System.out.println("进入等待");
Thread.sleep(1*50);
for (int i = 0; i < recordPool.size(); ) {
if (recordPool.get(i).isClosed() || recordPool.get(i)==null) {
recordPool.remove(i);
} else{
i++;
}
}
if(pool.size() != 0){
recordPool.add(pool.get(0));
return pool.remove(0);
}
if(recordPool.size() < newPoolSize){
Connection conn = load();
recordPool.add(conn);
return conn;
}
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*拿到新建连接*/
private Connection load(){
try {
Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
结束。
笔者能力有限,不足之处欢迎指出。