線程池-鏈接池-JDBC實例-JDBC鏈接池技術

線程池和鏈接池
  
線程池的原理:    
      來看一下線程池到底是怎麼一回事?其實線程池的原理很簡單,相似於操做系統中的緩衝區的概念,它的流程以下:先啓動若干數量的線程,並讓這些線程都處於睡眠狀態,當客戶端有一個新請求時,就會喚醒線程池中的某一個睡眠線程,讓它來處理客戶端的這個請求,當處理完這個請求後,線程又處於睡眠狀態。java

 爲何要使用線程池:mysql

  高峯期客戶端請求併發量大,若是爲每一個客戶端請求建立一個新線程的話,那耗費的CPU時間和內存將是驚人的,若是採用一個擁有多個線程的線程池,那將會節約大量的的系統資源,使得更多的CPU時間和內存用來處理實際的商業應用,而不是頻繁的線程建立與銷燬。

    數據庫鏈接池:
   一個數據庫鏈接對象均對應一個物理數據庫鏈接,每次操做都打開一個物理鏈接,使用完都關閉鏈接,這樣形成系統的 性能低下。web

 數據庫鏈接解決方案:sql

  數據庫鏈接池(Connection Pool)。系統初始運行時,主動創建足夠的鏈接,組成一個池.每次應用應用程序請求數據庫鏈接時,無需從新打開鏈接,而是從池中取出已有的鏈接,使用完後,再也不關閉,而是歸還。數據庫

  數據庫鏈接池的解決方案是在應用程序啓動時創建足夠的數據庫鏈接,並將這些鏈接組成一個鏈接池(簡單說:在一個「池」裏放了好多半成品的數據庫聯接對象),由應用程序動態地對池中的鏈接進行申請、使用和釋放。對於多於鏈接池中鏈接數的併發請求,應該在請求隊列中排隊等待。而且應用程序能夠根據池中鏈接的使用率,動態增長或減小池中的鏈接數。
  鏈接池技術儘量多地重用了消耗內存地資源,大大節省了內存,提升了服務器地服務效率,可以支持更多的客戶服務。經過使用鏈接池,將大大提升程序運行效率,同時,咱們能夠經過其自身的管理機制來監視數據庫鏈接的數量、使用狀況等。
    1) 最小鏈接數是鏈接池一直保持的數據庫鏈接,因此若是應用程序對數據庫鏈接的使用量不大,將會有大量的數據庫鏈接資源被浪費;
    2) 最大鏈接數是鏈接池能申請的最大鏈接數,若是數據庫鏈接請求超過此數,後面的數據庫鏈接請求將被加入到等待隊列中,這會影響以後的數據庫操做。apache

 爲何要使用鏈接池技術?設計模式

  這個能夠從數據庫鏈接缺陷和鏈接池優點來回答。tomcat

  數據庫鏈接缺陷: 一個數據庫鏈接對象均對應一個物理數據庫鏈接,每次操做都打開一個物理鏈接,使用完都關閉鏈接,這樣形成系統的 性能低下。服務器

  鏈接池技術:系統初始運行時,主動創建足夠的鏈接,組成一個池.每次應用應用程序請求數據庫鏈接時,無需從新打開鏈接,而是從池中取出已有的鏈接,使用完後,再也不關閉,而是歸還。併發

 鏈接池的組成部分:鏈接池的創建、鏈接池中鏈接的使用管理、鏈接池的關閉。

  1.鏈接池的創建
    在系統初始化時,根據相應的配置建立鏈接並放置在鏈接池中,以便須要使用時能從鏈接池中獲取,這樣就能夠避免鏈接隨意的創建、關閉形成的開銷。
  2.鏈接池中鏈接的使用管理
    鏈接池管理策略是鏈接池機制的核心。當鏈接池創建後,如何對鏈接池中的鏈接進行管理,解決好鏈接池內鏈接的分配和釋放,對系統的性能有很大的影響。鏈接的合理分配、釋放可提升鏈接的複用,下降了系統創建新鏈接的開銷,同時也加速了用戶的訪問速度。
        採用的方法是一個頗有名的設計模式:Reference Counting(引用記數)。該模式在複用資源方面應用的很是普遍,把該方法運用到對於鏈接的分配釋放上,爲每個數據庫鏈接,保留一個引用記數,用來記錄該鏈接的使用者的個數。
    (1)當客戶請求數據庫鏈接時,首先查看鏈接池中是否有空閒鏈接(指當前沒有分配出去的鏈接)。若是存在空閒鏈接,則把鏈接分配給客戶並做相應處理(即標記該鏈接爲正在使用,引用計數加1)。若是沒有空閒鏈接,則查看當前所開的鏈接數是否是已經達到maxConn(最大鏈接數),若是沒達到就從新建立一個鏈接給請求的客戶;若是達到就按設定的maxWaitTime(最大等待時間)進行等待,若是等待maxWaitTime後仍沒有空閒鏈接,就拋出無空閒鏈接的異常給用戶。
    (2)當客戶釋放數據庫鏈接時,先判斷該鏈接的引用次數是否超過了規定值,若是超過就刪除該鏈接,並判斷當前鏈接池內總的鏈接數是否小於minConn(最小鏈接數),若小於就將鏈接池充滿;若是沒超過就將該鏈接標記爲開放狀態,可供再次複用。能夠看出正是這套策略保證了數據庫鏈接的有效複用,避免頻繁地創建、釋放鏈接所帶來的系統資源開銷。
  3.鏈接池的關閉
    當應用程序退出時,應關閉鏈接池,此時應把在鏈接池創建時向數據庫申請的鏈接對象統一歸還給數據庫(即關閉全部數據庫鏈接),這與鏈接池的創建正好是一個相反過程。
    咱們採用DBCP(DataBase connection pool),數據庫鏈接池。DBCP(是 apache 上的一個 java 鏈接池項目,也是 tomcat 使用的鏈接池組件。單獨使用dbcp須要3個包:commons-dbcp.jar,commons-pool.jar,commons-collections.jar因爲創建數據庫鏈接是一個很是耗時耗資源的行爲,因此經過鏈接池預先同數據庫創建一些鏈接,放在內存中,應用程序須要創建數據庫鏈接時直接到鏈接池中申請一個就行,用完後再放回去。

 鏈接池的實現:

    1.使用Idea建立一個Maven項目,以下是Maven的項目結構:

    2.resources文件夾下面的db.properties文件

1 jdbc.driver=com.mysql.cj.jdbc.Driver
2 jdbc.url=jdbc:mysql://localhost:3306/crm01?useUnicode=true&characterEncoding=utf8
3 jdbc.user=root
4 jdbc.password=123456
5 initsize=1
6 maxactive=1
7 maxwait=5000
8 maxidle=1
9 minidle=1
#dbcp的基本配置的介紹
#1.initialSize :鏈接池啓動時建立的初始化鏈接數量(默認值爲0)
#2.maxActive :鏈接池中可同時鏈接的最大的鏈接數(默認值爲8,調整爲20,高峯單機器在20併發左右,本身根據應用場景定)
#3.maxIdle:鏈接池中最大的空閒的鏈接數,超過的空閒鏈接將被釋放,若是設置爲負數表示不限制
#(默認爲8個,maxIdle不能設置過小,由於假如在高負載的狀況下,鏈接的打開時間比關閉的時間快,會引發鏈接池中idle的個數 上升超過maxIdle,而形成頻繁的鏈接銷燬和建立,相似於jvm參數中的Xmx設置)
#4.minIdle:鏈接池中最小的空閒的鏈接數,低於這個數量會被建立新的鏈接(
#默認爲0,調整爲5,該參數越接近maxIdle,性能越好,由於鏈接的建立和銷燬,都是須要消耗資源的;可是不能太大,由於在機器很空閒的時候,也會建立低於minidle個數的鏈接,相似於jvm參數中的Xmn設置)
#5.maxWait  :最大等待時間,當沒有可用鏈接時,鏈接池等待鏈接釋放的最大時間,超過該時間限制會拋出異常,
#若是設置-1表示無限等待(默認爲無限,調整爲60000ms,避免因線程池不夠用,而致使請求被無限制掛起)

  

  3.DBUtil.java

package com.yuanziren;
import org.apache.commons.dbcp.BasicDataSource;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 使用鏈接池技術管理數據庫鏈接
 */
public class DBUtil {
    
    //數據庫鏈接池
    private static BasicDataSource dbcp;
    
    //爲不一樣線程管理鏈接
    private static ThreadLocal<Connection> tl;
    
    //經過配置文件來獲取數據庫參數
    static{
        try{
            Properties prop = new Properties();
            InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            prop.load(is);
            is.close();
            //1、初始化鏈接池
            dbcp = new BasicDataSource();
            //設置驅動 (Class.forName())
            dbcp.setDriverClassName(prop.getProperty("jdbc.driver"));
            //設置url
            dbcp.setUrl(prop.getProperty("jdbc.url"));
            //設置數據庫用戶名
            dbcp.setUsername(prop.getProperty("jdbc.user"));
            //設置數據庫密碼
            dbcp.setPassword(prop.getProperty("jdbc.password"));
            //初始鏈接數量
            dbcp.setInitialSize(Integer.parseInt(prop.getProperty("initsize")));
            //鏈接池容許的最大鏈接數
            dbcp.setMaxActive(Integer.parseInt(prop.getProperty("maxactive")));
            //設置最大等待時間
            dbcp.setMaxWait(Integer.parseInt(prop.getProperty("maxwait")));
            //設置最小空閒數
            dbcp.setMinIdle(Integer.parseInt(prop.getProperty("minidle")));
            //設置最大空閒數
            dbcp.setMaxIdle(Integer.parseInt(prop.getProperty("maxidle")));
            //初始化線程本地
            tl = new ThreadLocal<Connection>();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    /**
     * 獲取數據庫鏈接
     * @return
     * @throws SQLException 
     */
    public static Connection getConnection() throws SQLException {
        /*
         * 經過鏈接池獲取一個空閒鏈接
         */
        Connection conn = dbcp.getConnection();
        tl.set(conn);
        return conn;
    }

    /**
     * 關閉數據庫鏈接
     */
    public static void closeConnection(){
        try{
            Connection conn = tl.get();
            if(conn != null){
                /*
                 * 經過鏈接池獲取的Connection
                 * 的close()方法實際上並無將
                 * 鏈接關閉,而是將該連接歸還。
                 */
                conn.close();
                tl.remove();
            }    
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    /**
     * 測試是否鏈接成功
     * @param args
     * @throws SQLException
     */
    public static void main(String[] args) throws SQLException {
        System.out.println(getConnection());
    }
}

    4.pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 
 3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5   <modelVersion>4.0.0</modelVersion>
 6 
 7   <groupId>com.yuanziren</groupId>
 8   <artifactId>dbcp</artifactId>
 9   <version>1.0-SNAPSHOT</version>
10 
11   <name>dbcp</name>
12   <!-- FIXME change it to the project's website -->
13   <url>http://www.example.com</url>
14 
15   <properties>
16     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17     <maven.compiler.source>1.8</maven.compiler.source>
18     <maven.compiler.target>1.8</maven.compiler.target>
19   </properties>
20 
21   <dependencies>
22     <dependency>
23       <groupId>junit</groupId>
24       <artifactId>junit</artifactId>
25       <version>4.12</version>
26       <scope>test</scope>
27     </dependency>
28     <dependency>
29       <groupId>commons-dbcp</groupId>
30       <artifactId>commons-dbcp</artifactId>
31       <version>1.4</version>
32     </dependency>
33     <dependency>
34       <groupId>org.apache.commons</groupId>
35       <artifactId>commons-pool2</artifactId>
36       <version>2.6.2</version>
37     </dependency>
38     <dependency>
39       <groupId>org.apache.commons</groupId>
40       <artifactId>commons-collections4</artifactId>
41       <version>4.3</version>
42     </dependency>
43     <dependency>
44       <groupId>mysql</groupId>
45       <artifactId>mysql-connector-java</artifactId>
46       <version>8.0.16</version>
47     </dependency>
48   </dependencies>
49 
50   <build>
51   </build>
52 </project>

    4.運行結果

    5.代碼實現中遇到的Bug

      參考博客:

        JDBC鏈接MYSQL數據庫失敗:Loading class `com.mysql.jdbc.Driver'. This is deprecated.

        https://blog.csdn.net/weixin_42323802/article/details/82589743

        空指針問題:java.lang.NullPointerException at java.util.Properties$LineReader.readLine(Properties.java:434)問題

          https://blog.csdn.net/qq_41562136/article/details/83722473

        時區問題:

          https://blog.csdn.net/yongjiutongmi53151/article/details/86504546

相關文章
相關標籤/搜索