自定义连接池

139 阅读2分钟

    工作中我们都会用到各种各样的池技术,像数据库连接池、Redis连接池、线程池等。池技术的原理简单来说就是将创建好的对象缓存到一个“池子”里,需要使用对象时就从池子里获取,不用再走重复的创建、销毁等过程,以提高应用程序的性能。接下来我们DIY一个对象池,从而了解其实现原理。

  1. 定义一个对象池。一个最简单的对象池就两个方法,借用对象和归还对象。
public class ObjectPool<T> {
    /**
     * 空闲对象,PoolObject是对对象的进一步封装,方便添加扩展参数
     */
    private LinkedBlockingDeque<PoolObject<T>> idleObjects;
    /**
     * 所有的对象
     */
    private Map<Integer, PoolObject<T>> allObjects;
    /**
     * 池大小
     */
    private Integer poolSize;
    /**
     * 对象工厂,用于创建对象
     */
    private PoolObjectFactory<T> factory;

    public ObjectPool(Integer poolSize, PoolObjectFactory<T> factory) {
        this.poolSize = poolSize;
        this.factory = factory;
        idleObjects = new LinkedBlockingDeque<>(poolSize);
        allObjects = new ConcurrentHashMap<>();
    }

    /**
     * 从连接池里拿一个对象
     * @return
     */
    public T borrowObject() {
        PoolObject<T> p = null;
        while (true) {
            // 从空闲队列中取对象,如果取到则直接返回
            p = idleObjects.pollFirst();
            if (p != null) {
                System.out.println(Thread.currentThread().getName() + "获取到对象:" + p);
                return p.getObject();
            }
            synchronized (this) {
                if (poolSize > allObjects.size()) {
                    // 如果还没有达到池的大小则执行创建对象,并将创建好的对象放入池中
                    p = factory.makeObject();
                    allObjects.put(System.identityHashCode(p.getObject()), p);
                    idleObjects.push(p);
                } else {
                    // 如果池已经满了,则进入等待
                    try {
                        System.out.println(Thread.currentThread().getName() + "进入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 归还对象
     * @param obj
     */
    public void returnObject(T obj) {
        PoolObject<T> p = allObjects.get(System.identityHashCode(obj));
        if (p == null) {
            return;
        }
        System.out.println(Thread.currentThread().getName() + "归还对象:" + p);
        idleObjects.add(p);
        synchronized (this) {
            this.notifyAll();
        }
    }
}
  1. 对象的封装。这里对创建的对象做一些封装,方便可以扩展其他参数。
public class PoolObject<T> {
    /**
     * 对象
     */
    private T object;

    public PoolObject(T object) {
        this.object = object;
    }

    public T getObject() {
        return object;
    }

    public void setObject(T object) {
        this.object = object;
    }
}
  1. 创建对象的工厂
public interface PoolObjectFactory<T> {
    /**
     * 创建对象
     * @return
     */
    PoolObject<T> makeObject();
}
  1. 测试
public static void main(String[] args) {
    ObjectPool<Integer> pool = new ObjectPool<>(2, new PoolObjectFactory<Integer>() {
        @Override
        public PoolObject<Integer> makeObject() {
            return new PoolObject<>(1);
        }
    });

    for (int i = 0; i < 5; i++) {
        new Thread(()->{
            Integer obj = pool.borrowObject();
            try {
                Thread.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            pool.returnObject(obj);
        }).start();
    }
}

执行效果:

Thread-2获取到对象:com.gitee.bailiny.PoolObject@579b39be
Thread-3获取到对象:com.gitee.bailiny.PoolObject@67b23274
Thread-0进入等待
Thread-4进入等待
Thread-1进入等待
Thread-3归还对象:com.gitee.bailiny.PoolObject@67b23274
Thread-4进入等待
Thread-1获取到对象:com.gitee.bailiny.PoolObject@67b23274
Thread-0进入等待
Thread-2归还对象:com.gitee.bailiny.PoolObject@579b39be
Thread-0获取到对象:com.gitee.bailiny.PoolObject@579b39be
Thread-4进入等待
Thread-0归还对象:com.gitee.bailiny.PoolObject@579b39be
Thread-4获取到对象:com.gitee.bailiny.PoolObject@579b39be
Thread-1归还对象:com.gitee.bailiny.PoolObject@67b23274
Thread-4归还对象:com.gitee.bailiny.PoolObject@579b39be

以上就是自定义一个对象池的最简单实现,实际上代码参考了common-pool2中GenericObjectPool的实现。只不过在common-pool2中还有更丰富的功能,比如等待超时、最大容量、空闲容量、自定义参数配置等。