自定义简单连接池

前言

实现基本的获取连接和归还连接的功能

自定义连接池

MockConnection

public class MockConnection implements Connection {

    private String name;

    public MockConnection(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MockConnection{" +
                "name='" + name + '\'' +
                '}';
    }
 
    // 省略实现的部分
}

Pool

package cn.knightzz.flyweight.pool;

import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * @author 王天赐
 * @title: Pool
 * @projectName hm-juc-codes
 * @description: 自定义连接池
 * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
 * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
 * @create: 2022-08-20 19:51
 */
@Slf4j(topic = "Pool")
public class Pool {

    /**
     * 定义线程池大小
     */
    private int poolSize;

    /**
     * 定义连接对象数组
     */
    Connection[] connections;

    /**
     * 连接状态数组 0表示空闲, 1表示繁忙
     */
    private AtomicIntegerArray status;

    public Pool(int poolSize) {

        // 检查poolSize是否合法
        checkPoolSize(poolSize);

        this.poolSize = poolSize;
        this.connections = new Connection[poolSize];
        this.status = new AtomicIntegerArray(poolSize);

        // 初始化创建Connection
        for (int i = 0; i < poolSize; i++) {
            connections[i] = new MockConnection("Connection : " + i);
        }
    }

    private void checkPoolSize(int poolSize) {
        if (poolSize < 0) {
            throw new RuntimeException("poolSize 非法 : poolSize = " + poolSize);
        }
    }

    /**
     * 获取连接
     *
     * @return 连接对象 java.sql.Connection
     */
    public Connection borrow() {

        // 基本思路 :
        // 1. 遍历所有的状态数组, 获取状态为0的空闲连接
        // 2. 如果有空闲连接直接返回 , 如果没有, 直接wait
        // 3. 在最外层增加 while(true) 防止虚假唤醒

        while (true) {

            for (int i = 0; i < poolSize; i++) {
                if (status.get(i) == 0) {
                    // 修改状态数组
                    // 这里要保证原子操作, 防止并发环境下 : 状态修改了, 但是没有连接, 或者同一个连接被重复获取
                    if (status.compareAndSet(i, 0, 1)) {
                        log.debug("获取连接成功 : {} ", connections[i]);
                        return connections[i];
                    }
                }
            }

            // 如果暂时没有空闲连接, 就直接wait
            // 这里需要注意的是, Pool 是多和线程共享同一个对象, wait操作需要加锁, 否则可能出现重复wait
            synchronized (this) {
                try {
                    log.debug("当前暂无空闲连接, wait中 .. .");
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }


    }

    /**
     * 释放连接
     *
     * @param connection 待释放掉的连接对象
     */
    public void free(Connection connection) {

        // 基本思路 :
        // 1. 遍历所有的连接数组, 找到当前的连接
        // 2. 设置当前的连接数组的状态
        // 3. 唤醒所有等待的线程, 注意啊 : 要加锁, 防止多次重复唤醒

        for (int i = 0; i < poolSize; i++) {
            if (connections[i] == connection) {
                // 修改状态
                if (status.compareAndSet(i, 1, 0)) {
                    // 唤醒其他的线程
                    // 加锁避免重复notify
                    synchronized (this) {
                        log.debug("{} 释放连接成功, 唤醒其他线程 ", connection);
                        this.notifyAll();
                    }
                    break;
                }
            }
        }
    }


}

获取连接基本思路 :

  1. 遍历所有的状态数组, 获取状态为0的空闲连接
  2. 如果有空闲连接直接返回 , 如果没有, 直接wait
  3. 在最外层增加 while(true) 防止虚假唤醒

测试代码

package cn.knightzz.flyweight.pool;

import java.sql.Connection;
import java.util.Random;

@SuppressWarnings("all")
public class TestPool {

    public static void main(String[] args) {

        Pool pool = new Pool(2);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                Connection conn = pool.borrow();
                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                pool.free(conn);
            }, "t" + i).start();
        }
    }
}

image-20220820202548533

总结

以上实现没有考虑:

  • 连接的动态增长与收缩

  • 连接保活(可用性检测)

  • 等待超时处理

  • 分布式 hash

对于关系型数据库,有比较成熟的连接池实现,例如c3p0, druid等 对于更通用的对象池,可以考虑使用apache

commons pool,例如redis连接池可以参考jedis中关于连接池的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兀坐晴窗独饮茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值