java并发实战:连接池实现

池化技术简介

在我们使用数据库的过程中,我们往往使用数据库连接池而不是直接使用数据库连接进行操作,这是因为每一个数据库连接的创建和销毁的代价是昂贵的,而池化技术则预先创建了资源,这些资源是可复用的,这样就保证了在多用户情况下只能使用指定数目的资源,避免了一个用户创建一个连接资源,造成程序运行开销过大。关于Java并发编程的总结和思考

连接池实现原理

这里只实现一个简易的连接池,更多复杂的需求可根据该连接池进行改进,该连接池主要参数如下:

  1. 一个繁忙队列busy
  2. 一个空闲队列idle
  3. 连接池最大活动连接数maxActive
  4. 连接池最大等待时间maxWait
  5. 连接池的活动连接数activeSize

程序流程图如下:

java并发实战:连接池实现

代码实现

泛型接口ConnectionPool.java


public interface ConnectionPool<T> {

    /**
     * &#x521D;&#x59CB;&#x5316;&#x6C60;&#x8D44;&#x6E90;
     * @param maxActive &#x6C60;&#x4E2D;&#x6700;&#x5927;&#x6D3B;&#x52A8;&#x8FDE;&#x63A5;&#x6570;
     * @param maxWait &#x6700;&#x5927;&#x7B49;&#x5F85;&#x65F6;&#x95F4;
     */
    void init(Integer maxActive, Long maxWait);

    /**
     * &#x4ECE;&#x6C60;&#x4E2D;&#x83B7;&#x53D6;&#x8D44;&#x6E90;
     * @return &#x8FDE;&#x63A5;&#x8D44;&#x6E90;
     */
    T getResource() throws Exception;

    /**
     * &#x91CA;&#x653E;&#x8FDE;&#x63A5;
     * @param connection &#x6B63;&#x5728;&#x4F7F;&#x7528;&#x7684;&#x8FDE;&#x63A5;
     */
    void release(T connection) throws Exception;

    /**
     * &#x91CA;&#x653E;&#x8FDE;&#x63A5;&#x6C60;&#x8D44;&#x6E90;
     */
    void close();

}

以zookeeper为例,实现zookeeper连接池,ZookeeperConnectionPool.java


public class ZookeeperConnectionPool implements ConnectionPool<ZooKeeper> {
    //&#x6700;&#x5927;&#x6D3B;&#x52A8;&#x8FDE;&#x63A5;&#x6570;
    private Integer maxActive;
    //&#x6700;&#x5927;&#x7B49;&#x5F85;&#x65F6;&#x95F4;
    private Long maxWait;
    //&#x7A7A;&#x95F2;&#x961F;&#x5217;
    private LinkedBlockingQueue<ZooKeeper> idle = new LinkedBlockingQueue<>();
    //&#x7E41;&#x5FD9;&#x961F;&#x5217;
    private LinkedBlockingQueue<ZooKeeper> busy = new LinkedBlockingQueue<>();
    //&#x8FDE;&#x63A5;&#x6C60;&#x6D3B;&#x52A8;&#x8FDE;&#x63A5;&#x6570;
    private AtomicInteger activeSize = new AtomicInteger(0);
    //&#x8FDE;&#x63A5;&#x6C60;&#x5173;&#x95ED;&#x6807;&#x8BB0;
    private AtomicBoolean isClosed = new AtomicBoolean(false);
    //&#x603B;&#x5171;&#x83B7;&#x53D6;&#x7684;&#x8FDE;&#x63A5;&#x8BB0;&#x6570;
    private AtomicInteger createCount = new AtomicInteger(0);
    //&#x7B49;&#x5F85;zookeeper&#x5BA2;&#x6237;&#x7AEF;&#x521B;&#x5EFA;&#x5B8C;&#x6210;&#x7684;&#x8BA1;&#x6570;&#x5668;
    private static ThreadLocal<CountDownLatch> latchThreadLocal = ThreadLocal.withInitial(() -> new CountDownLatch(1));

    public ZookeeperConnectionPool(Integer maxActive, Long maxWait) {
        this.init(maxActive, maxWait);
    }

    @Override
    public void init(Integer maxActive, Long maxWait) {
        this.maxActive = maxActive;
        this.maxWait = maxWait;
    }

    @Override
    public ZooKeeper getResource() throws Exception {
        ZooKeeper zooKeeper;
        Long nowTime = System.currentTimeMillis();
        final CountDownLatch countDownLatch = latchThreadLocal.get();

        //&#x7A7A;&#x95F2;&#x961F;&#x5217;idle&#x662F;&#x5426;&#x6709;&#x8FDE;&#x63A5;
        if ((zooKeeper = idle.poll()) == null) {
            //&#x5224;&#x65AD;&#x6C60;&#x4E2D;&#x8FDE;&#x63A5;&#x6570;&#x662F;&#x5426;&#x5C0F;&#x4E8E;maxActive
            if (activeSize.get() < maxActive) {
                //&#x5148;&#x589E;&#x52A0;&#x6C60;&#x4E2D;&#x8FDE;&#x63A5;&#x6570;&#x540E;&#x5224;&#x65AD;&#x662F;&#x5426;&#x5C0F;&#x4E8E;&#x7B49;&#x4E8E;maxActive
                if (activeSize.incrementAndGet() <= maxActive) {
                    //&#x521B;&#x5EFA;zookeeper&#x8FDE;&#x63A5;
                    zooKeeper = new ZooKeeper("localhost", 5000, (watch) -> {
                        if (watch.getState() == Watcher.Event.KeeperState.SyncConnected) {
                            countDownLatch.countDown();
                        }
                    });
                    countDownLatch.await();
                    System.out.println("Thread:" + Thread.currentThread().getId() + "&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#xFF1A;" + createCount.incrementAndGet() + "&#x6761;");
                    busy.offer(zooKeeper);
                    return zooKeeper;
                } else {
                    //&#x5982;&#x589E;&#x52A0;&#x540E;&#x53D1;&#x73B0;&#x5927;&#x4E8E;maxActive&#x5219;&#x51CF;&#x53BB;&#x589E;&#x52A0;&#x7684;
                    activeSize.decrementAndGet();
                }
            }
            //&#x82E5;&#x6D3B;&#x52A8;&#x7EBF;&#x7A0B;&#x5DF2;&#x6EE1;&#x5219;&#x7B49;&#x5F85;busy&#x961F;&#x5217;&#x91CA;&#x653E;&#x8FDE;&#x63A5;
            try {
                System.out.println("Thread:" + Thread.currentThread().getId() + "&#x7B49;&#x5F85;&#x83B7;&#x53D6;&#x7A7A;&#x95F2;&#x8D44;&#x6E90;");
                Long waitTime = maxWait - (System.currentTimeMillis() - nowTime);
                zooKeeper = idle.poll(waitTime, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                throw new Exception("&#x7B49;&#x5F85;&#x5F02;&#x5E38;");
            }
            //&#x5224;&#x65AD;&#x662F;&#x5426;&#x8D85;&#x65F6;
            if (zooKeeper != null) {
                System.out.println("Thread:" + Thread.currentThread().getId() + "&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#xFF1A;" + createCount.incrementAndGet() + "&#x6761;");
                busy.offer(zooKeeper);
                return zooKeeper;
            } else {
                System.out.println("Thread:" + Thread.currentThread().getId() + "&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#x8D85;&#x65F6;&#xFF0C;&#x8BF7;&#x91CD;&#x8BD5;&#xFF01;");
                throw new Exception("Thread:" + Thread.currentThread().getId() + "&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#x8D85;&#x65F6;&#xFF0C;&#x8BF7;&#x91CD;&#x8BD5;&#xFF01;");
            }
        }
        //&#x7A7A;&#x95F2;&#x961F;&#x5217;&#x6709;&#x8FDE;&#x63A5;&#xFF0C;&#x76F4;&#x63A5;&#x8FD4;&#x56DE;
        busy.offer(zooKeeper);
        return zooKeeper;
    }

    @Override
    public void release(ZooKeeper connection) throws Exception {
        if (connection == null) {
            System.out.println("connection &#x4E3A;&#x7A7A;");
            return;
        }
        if (busy.remove(connection)){
            idle.offer(connection);
        } else {
            activeSize.decrementAndGet();
            throw new Exception("&#x91CA;&#x653E;&#x5931;&#x8D25;");
        }
    }

    @Override
    public void close() {
        if (isClosed.compareAndSet(false, true)) {
            idle.forEach((zooKeeper) -> {
                try {
                    zooKeeper.close();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            busy.forEach((zooKeeper) -> {
                try {
                    zooKeeper.close();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

测试用例

这里创建20个线程并发测试连接池,Test.java


public class Test {

    public static void main(String[] args) throws Exception {
        int threadCount = 20;
        Integer maxActive = 10;
        Long maxWait = 10000L;
        ZookeeperConnectionPool pool = new ZookeeperConnectionPool(maxActive, maxWait);
        CountDownLatch countDownLatch = new CountDownLatch(20);
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                countDownLatch.countDown();
                try {
                    countDownLatch.await();
                    ZooKeeper zooKeeper = pool.getResource();
                    Thread.sleep(2000);
                    pool.release(zooKeeper);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }).start();
        }
        while (true){

        }
    }
}

来源:https://segmentfault.com/a/1190000017926611

Original: https://www.cnblogs.com/lalalagq/p/10286937.html
Author: sfornt
Title: java并发实战:连接池实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/537940/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 实力总结四类Bean注入Spring的方式

    一提到 Spring,大家最先想到的是啥?是 AOP和 IOC的两大特性?是 Spring中 Bean的初始化流程?还是基于 Spring的 Spring Cloud全家桶呢? 今…

    Java 2023年6月5日
    0106
  • SpringBoot+MDC实现全链路调用日志跟踪

    实现HttpRequestInterceptor接口并重写process方法 ​ 如果调用线程中含有traceId,则需要将获取到的traceId通过request中的header…

    Java 2023年5月30日
    046
  • c#反射

    待总结 posted @2015-03-19 12:58 zhepama 阅读(134 ) 评论() 编辑 Original: https://www.cnblogs.com/zh…

    Java 2023年5月30日
    084
  • VMware虚拟机中安装Linux操作系统(ubuntu)

    一、准备工作: 1、下载VMware虚拟机 下载地址:https://www.vmware.com/cn/products/workstation-pro/workstation-…

    Java 2023年6月9日
    061
  • requested upstream branch ‘origin/master’ does not exist

    本地的commit 本地仓库有文件,远程仓库也有文件,正确姿势: 1,git remote add origin 远程仓库地址 2,git pull origin master &…

    Java 2023年6月13日
    066
  • springboot分析——自定义启动类

    在实际开发过程中,如果有一些公共功能,我们可以单独封装,然后配置成starter启动类,其他的项目需要使用时,主要 只要依赖开启就可以了。下面我们自定义一个自动配置启动类。 一:自…

    Java 2023年5月30日
    064
  • JavaWeb之如何把请求数据转成实体类

    JavaWeb之如何把请求数据转成实体类 自己写个工具类加入下面两个静态方法 自定一个注解类DateTimeFormatting 调用方式 User user = util.Obj…

    Java 2023年6月5日
    059
  • 微服务网关Gateway实践总结

    有多少请求,被网关截胡; 一、Gateway简介 微服务架构中,网关服务通常提供动态路由,以及流量控制与请求识别等核心能力,在之前的篇幅中有说过Zuul组件的使用流程,但是当下Ga…

    Java 2023年6月15日
    070
  • Java中带包(创建及引用)的类的编译与调试

    java源程序的编译大家都知道,也就是cmd中到源文件所在目录下javac .java即可,当程序中有 包声明还能简简单单的直接javac .java吗?答案当然是no,下面举个简…

    Java 2023年5月29日
    065
  • 6、Arrays类

    常用方法 toString 返回数组的字符串形式 Arrays.toString(arr) Integer[] integers = {1, 20, 90}; System.out…

    Java 2023年6月7日
    070
  • 第三周

    第三周 1.测试成功的接口再次测试报错 原因:之前在查询时更改了方法,由Mybatis Plus 查询的方式改为了xml,同时在实体类中添加了字段做连表查询,导致之前所有用Myba…

    Java 2023年6月7日
    083
  • InnoDB学习(一)之BufferPool

    我们知道InnoDB数据库的数据是持久化在磁盘上的,而磁盘的IO速度很慢,如果每次数据库访问都直接访问磁盘,显然严重影响数据库的性能。为了提升数据库的访问性能,InnoDB为数据库…

    Java 2023年6月8日
    071
  • Spring Ioc源码分析系列–Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理

    上一篇分析了 BeanFactoryPostProcessor的作用,那么这一篇继续在 refresh()方法里游荡,相信对Spring熟悉点的朋友,在看完 BeanFactory…

    Java 2023年6月8日
    088
  • Java学习-动手动脑2

    public void println() { newLine(); } /** * Prints a boolean and then terminate the line. T…

    Java 2023年6月9日
    067
  • 设计模式 19 备忘录模式

    备忘录模式(Memento Pattern)属于 行为型模式 2021 年 10 月 1 日下午,河南驻马店的一名 13 岁女中学生,因和同学发生不愉快喝下半瓶 百草枯。10 月 …

    Java 2023年6月6日
    072
  • MySQL、Oracle元数据抽取分析

    最近接到个任务是抽取mysql和Oracle的元数据,大致就是在库里把库、schema、表、字段、分区、索引、主键等信息抽取出来,然后导成excel。 因为刚开始接触元数据,对这个…

    Java 2023年6月5日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球