【Java】连接池、线程池 各种池【转+整理】与享元模式。

谈谈连接池、线程池技术原理

 
 
  做互联网研发,最早接触使用jdbc技术,为了数据库连接能够复用,会用到c3p0、dbcp等数据库连接池。应该是研发人员最早接触的数据库连接池,再到httpclient http连接池,再到微服务netty连接池redis客户端连接池,以及jdk中线程池技术。

   这么多数据库、http、netty连接池,jdk线程池,本质上都是连接池技术,连接池技术核心连接或者说创建的资源复用

   连接池技术核心:通过减少连接创建、关闭来提升性能。用于用户后续使用,好处是后续使用不用在创建连接以及线程,因为这些都需要相关很多文件、连接资源、操作系统内核资源支持来完成构建,会消耗大量资源,并且创建、关闭会消耗应用程序大量性能。

   网络连接本身会消耗大量内核资源,在linux系统下,网络连接创建本身tcp/ip协议栈在内核里面,连接创建关闭会消耗大量文件句柄(linux中万物皆文件,一种厉害抽象手段)系统资源。当下更多是应用tcp技术完成网络传输,反复打开关闭,需要操作系统维护大量tcp协议栈状态。

   连接池本质上是构建一个容器,容器来存储创建好的线程、http连接、数据库连接、netty连接等。对于使用方相当于黑盒,按照接口进行使用就可以了。各个连接池构建、使用管理详细过程大概分成以下三部分。

   第一部分:首先初始化连接池,根据设置相应参数,连接池大小、核心线程数、核心连接数等参数,初始化创建数据库、http、netty连接以及jdk线程。

   第二部分:连接池使用,前边初始化好的连接池、线程池,直接从连接池、线程中取出资源即可进行使用,使用完后要记得交还连接池、线程池,通过池容器来对资源进行管理。

   第三部分:对于连接池维护,连接池、线程池来维护连接、线程状态,不可用连接、线程进行销毁,正在使用连接、线程进行状态标注,连接、线程不够后并且少于设置最大连接、线程数,要进行新连接、线程创建。

   通过上边可以了解到各种连接池技术以及线程池原理或者说套路,理解原理才能不被纷繁复杂表象掩盖。

   下面谈谈构建自己连接池,其实理解了连接池、线程原理,可以使用ArrayList来构建自己连接池、线程池。初始化时创建配置连接数、线程,存储在ArrayList容器中,使用时从ArrayList从取出连接、线程进行使用,执行完任务后,提交回ArrayList容器。前提条件是单线程,在多线程状态下要用线程安全容器

   前边根据原理介绍了一个简单连接池、线程池怎样构建,实际工业级别线程池还要考虑到连接状态,短连接重连,线程池维护管理高效,线程池稳定等多个因素。

   需要用到连接池而又没有相关开源产品可用时,java连接池可以使用common-pool2来构建,比如google开源gRPC技术,本身是高性能跨平台技术,但目前作为微服务使用,没有连接池、负载均衡等相应配套,这时可以根据需要自己基于Java容器构建自己连接池。也可以利用common-pool2构建连接池来提升应用性能,以及保持高可用。common-pool2本身不仅仅可以构建连接池使用,还可以用来构建对象池。

   连接池还有一个副作用就是实现了高可用,在微服务场景下一个连接不可用,那么再从netty连接池中取出一个进行使用,避免了连接不可用问题。

   掌握原理从比较全面掌握各种池技术,避免数据库连接池,再到httpclient http连接池,再到微服务netty连接池,redis客户端连接池,以及jdk中线程池,对象池各种各样池技术,使我们眼花缭乱,花费过多时间,掌握原理机制以不变应万变

   推广一下这个方法,其他技术也是类似,深入掌握其原理,就可以明白其他类似技术相似原理,避免疲于应对各种新技术。但每一种架构设计与实现又与领域有着关系,也不可讲原理不顾实际情况扩展。理论与架构设计、源码学习相结合才是最好的,希望有帮助。

 


 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。

一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,urldriverClassNameusernamepassworddbname这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

看个例子:

 

看下数据库连接池的代码:

[java] view plaincopy

  1. public class ConnectionPool {  
  2.       
  3.     private Vector<Connection> pool;  
  4.       
  5.     /*公有属性*/  
  6.     private String url = "jdbc:mysql://localhost:3306/test";  
  7.     private String username = "root";  
  8.     private String password = "root";  
  9.     private String driverClassName = "com.mysql.jdbc.Driver";  
  10.   
  11.     private int poolSize = 100;  
  12.     private static ConnectionPool instance = null;  
  13.     Connection conn = null;  
  14.   
  15.     /*构造方法,做一些初始化工作*/  
  16.     private ConnectionPool() {  
  17.         pool = new Vector<Connection>(poolSize);  
  18.   
  19.         for (int i = 0; i < poolSize; i++) {  
  20.             try {  
  21.                 Class.forName(driverClassName);  
  22.                 conn = DriverManager.getConnection(url, username, password);  
  23.                 pool.add(conn);  
  24.             } catch (ClassNotFoundException e) {  
  25.                 e.printStackTrace();  
  26.             } catch (SQLException e) {  
  27.                 e.printStackTrace();  
  28.             }  
  29.         }  
  30.     }  
  31.   
  32.     /* 返回连接到连接池 */  
  33.     public synchronized void release() {  
  34.         pool.add(conn);  
  35.     }  
  36.   
  37.     /* 返回连接池中的一个数据库连接 */  
  38.     public synchronized Connection getConnection() {  
  39.         if (pool.size() > 0) {  
  40.             Connection conn = pool.get(0);  
  41.             pool.remove(conn);  
  42.             return conn;  
  43.         } else {  
  44.             return null;  
  45.         }  
  46.     }  
  47. }  

通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

 

posted @ 2018-03-05 13:57  Legolas_4  阅读(2517)  评论(0编辑  收藏  举报