帶你手寫一個數據庫連接池


1.定義一個連接池接口

import java.sql.Connection;

/**
 * 數據庫連接池接口
 */
public interface DbPool {

    /**
     * 連接池初始化
     */
    void init();

    /**
     * 獲取一個連接
     *
     * @return
     */
    Connection getConnection();

    /**
     * 釋放一個連接
     *
     * @param connection
     */
    void releaseConnection(Connection connection);

    /**
     * 銷毀連接池
     */
    void destroy();
}

 

2.定義一個連接池的實現

import org.springframework.scheduling.annotation.Scheduled;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 數據庫連接池實現
 */
public class DbPoolImpl implements DbPool {

    /**
     * 空閑連接池
     */
    private LinkedBlockingQueue<Connection> idleConnectPool;

    /**
     * 活躍連接池
     */
    private LinkedBlockingQueue<Connection> busyConnectPool;

    /**
     * 當前正在被使用的連接數
     */
    private AtomicInteger activeSize = new AtomicInteger(0);

    /**
     * 最大連接數
     */
    private final int maxSize;

    public DbPoolImpl(int maxSize) {
        this.maxSize = maxSize;
        init();// init pool
    }

    /**
     * 連接池初始化
     */
    @Override
    public void init() {
        idleConnectPool = new LinkedBlockingQueue<>();
        busyConnectPool = new LinkedBlockingQueue<>();
    }

    /**
     * 獲取一個連接
     *
     * @return
     */
    @Override
    public Connection getConnection() {
        // 從idle池中取出一個連接
        Connection connection = idleConnectPool.poll();
        if (connection != null) {
            // 如果有連接,則放入busy池中
            busyConnectPool.offer(connection);
            System.out.println("獲取到連接");
            return connection;
        }

//        synchronized (DbPoolImpl.class) { // 鎖--效率低下
        // idle池中沒有連接
        // 如果idle池中連接未滿maxSize,就新建一個連接
        if (activeSize.get() < maxSize) {
            // 通過 activeSize.incrementAndGet() <= maxSize 這個判斷
            // 解決 if(activeSize.get() < maxSize) 存在的線程安全問題
            if (activeSize.incrementAndGet() <= maxSize) {
                connection = DbUtil.createConnection();// 創建新連接
                busyConnectPool.offer(connection);
                return connection;
            }
        }
//        }

        // 如果空閑池中連接數達到maxSize, 則阻塞等待歸還連接
        try {
            System.out.println("排隊等待連接");
            connection = idleConnectPool.poll(10000, TimeUnit.MILLISECONDS);// 阻塞獲取連接,如果10秒內有其他連接釋放,
            if (connection == null) {
                System.out.println("等待超時");
                throw new RuntimeException("等待連接超時");
            }
            System.out.println("等待到了一個連接");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 釋放一個連接
     *
     * @param connection
     */
    @Override
    public void releaseConnection(Connection connection) {
        if (connection != null) {
            busyConnectPool.remove(connection);
            idleConnectPool.offer(connection);
        }
    }

    /**
     * 銷毀連接池
     */
    @Override
    public void destroy() {

    }

    /**
     * 定時對連接進行健康檢查
     * 注意:只能對idle連接池中的連接進行健康檢查,
     * 不可以對busyConnectPool連接池中的連接進行健康檢查,因為它正在被客戶端使用;
     */
    //@Scheduled(fixedRate = 60 * 1000)
    public void check() {
        for (int i = 0; i < activeSize.get(); i++) {
            Connection connection = idleConnectPool.poll();
            try {
                boolean valid = connection.isValid(2000);
                if (!valid) {
                    // 如果連接不可用,則創建一個新的連接
                    connection = DbUtil.createConnection();
                }
                idleConnectPool.offer(connection);// 放進一個可用的連接
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

 

3.數據庫操作工具類

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class DbUtil {

    private static String driverClass = "com.mysql.jdbc.Driver";
    private static String url = "jdbc:mysql://localhost:3306/testdb";
    private static String username = "root";
    private static String password = "123456";

    /**
     * 創建數據庫連接
     *
     * @return
     */
    public static Connection createConnection() {
        Connection connection = null;
        try {
            Class.forName(driverClass);
            connection = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 執行插入操作
     *
     * @param connection
     * @param insertSql
     * @throws SQLException
     */
    public static void executeInsert(Connection connection, String insertSql) {
        try {
            Statement statement = connection.createStatement();
            statement.executeUpdate(insertSql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

4.在Spring中配置自己寫的連接池

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DbPoolConfig {

    @Bean
    public DbPool dbPool() {
        return new DbPoolImpl(10);
    }
}

 

5.測試接口

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Connection;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

@RestController
@RequestMapping("/test")
public class TestController {

    private static int executorThreadSize = 20;
    private static String insertSql = "INSERT INTO `user_tbl` (`name`) VALUES ('%s');";
    private CountDownLatch countDownLatch = new CountDownLatch(10);

    @Autowired
    private DbPool dbPool;

    @GetMapping("/pool")
    public String dbPoolTest() {
        System.out.println("請求線程:" + Thread.currentThread().getName());
        new Thread(new Runnable() {
            @Override
            public void run() {
                countDownLatch.countDown();
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < 10; i++) {
                    Connection connection = dbPool.getConnection();// 從連接池獲取連接
                    // 模擬插入數據
                    DbUtil.executeInsert(connection, String.format(insertSql, UUID.randomUUID().toString()));
                    dbPool.releaseConnection(connection);// 釋放連接
                }
            }
        }).start();
        return "success";
    }
}

 

6.有關數據庫表

DROP TABLE IF EXISTS `user_tbl`;
CREATE TABLE `user_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM