线程池和连接池
  
线程池的原理:    
      来看一下线程池究竟是怎么一回事?其实线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当客户端有一个新请求时,就会唤醒线程池中的某一个睡眠线程,让它来处理客户端的这个请求,当处理完这个请求后,线程又处于睡眠状态。

 为什么要使用线程池:

  高峰期客户端请求并发量大,如果为每个客户端请求创建一个新线程的话,那耗费的CPU时间和内存将是惊人的,如果采用一个拥有多个线程的线程池,那将会节约大量的的系统资源,使得更多的CPU时间和内存用来处理实际的商业应用,而不是频繁的线程创建与销毁。

    数据库连接池:
   一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。

 数据库连接解决方案:

  数据库连接池(Connection Pool)。系统初始运行时,主动建立足够的连接,组成一个池.每次应用应用程序请求数据库连接时,无需重新打开连接,而是从池中取出已有的连接,使用完后,不再关闭,而是归还。

  数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
  连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
    1) 最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费;
    2) 最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。

 为什么要使用连接池技术?

  这个可以从数据库连接缺陷和连接池优势来回答。

  数据库连接缺陷: 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。

  连接池技术:系统初始运行时,主动建立足够的连接,组成一个池.每次应用应用程序请求数据库连接时,无需重新打开连接,而是从池中取出已有的连接,使用完后,不再关闭,而是归还。

 连接池的组成部分:连接池的建立、连接池中连接的使用管理、连接池的关闭。

  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();
            //一、初始化连接池
            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