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;