java:基于LinkedBlockingQueue实现的资源池

版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/79277610

假有这样一个需求:

有一组类型为R固定数目的资源对象,多个线程在使用资源对象r时需要申请取用一个资源对象,用完后还回以便其他线程使用。

这个需求很简单,用commons-pool就可以实现,但仅为了这个需求就增加一个jar依赖,有点不划算,所以我基于LinkedBlockingQueue设计了一个资源池对象(resource pool)对象来实现这个需求。 资源池对象有两个基本的方法apply()/free()分别用于申请和释放资源。用一个LinkedBlockingQueue类型的queue来保存空闲的资源对象

  • apply() 从资源队列queue中申请一个资源,如果队列为空,线程阻塞,否则就从队列头部取出一个对象,保存在TLS变量中
  • free() 归还资源对象,将TLS变量中保存的资源对象重新加入queue尾部。

apply()/free()必须成对使用

以下是实现代码 ResourcePool.java

/**
 * 资源池管理对象<br>
 * {@link #apply()},{@link #free()}用于申请/释放资源,申请的资源对象不可跨线程调用,<br>
 * 通过重写{@link #isNestable()}方法决定是否允许嵌套调用
 * @author guyadong
 *
 * @param <R> 资源类型
 */
public class ResourcePool<R>{
    /** 资源队列 */
    protected final LinkedBlockingQueue<R> queue = new LinkedBlockingQueue<R>();
    /** 当前线程申请的资源对象 */
    private final ThreadLocal<R> tlsResource = new ThreadLocal<R>();
    /** 线程嵌套计数 */
    private final ThreadLocal<AtomicInteger> threadNestCount= new ThreadLocal<AtomicInteger>();
    private final boolean nestable = isNestable();
    protected ResourcePool() {
    }
    /**
     * 构造方法
     * @param resources 资源对象集合
     * @throws IllegalArgumentException 包含{@code null}元素
     */
    public ResourcePool(Collection<R> resources){
        for(R r:resources){
            if(null == r){
                throw new IllegalArgumentException("resources contains null element");
            }
            queue.add(r);
        }
    }
    @SafeVarargs
    public ResourcePool( R ...resources ){
        this(Arrays.asList(resources));
    }
    /**
     * 从资源队列{@link #queue}中取出一个对象,保存到{@link #tlsResource}
     * @return
     * @throws InterruptedException
     */
    private R getResource() throws InterruptedException{
        if(null != tlsResource.get()){
            // 资源状态异常
            throw new IllegalStateException("INVALID tlsResource state");
        }
        R r;
        if(queue.isEmpty() && null != (r = newResource())){
            queue.offer(r);
        }
        r = open(queue.take());
        tlsResource.set(r);
        return r;
    }
    /**
     * 将{@link #tlsResource}中的资源对象重新加入资源队列{@link #queue},并清除TLS变量{@link #tlsResource}
     */
    private void recycleResource(){
        R r = tlsResource.get();
        if(null == r){
            // 资源状态异常
            throw new IllegalStateException("INVALID tlsResource while recycle");
        }
        // 放回队例
        queue.offer(close(r));
        tlsResource.remove();
    }
    /**
     * (阻塞式)申请当前线程使用的资源对象,不可跨线程使用
     * @return
     * @throws InterruptedException
     */
    public final R applyChecked() throws InterruptedException{
        if(nestable){
            AtomicInteger count = threadNestCount.get();
            if(null == count){
                // 当前线程第一次申请资源
                count = new AtomicInteger(1);
                threadNestCount.set(count);
                return getResource();
            }else{
                // 嵌套调用时直接返回TLS变量
                if(null == this.tlsResource.get()){
                    // 资源状态异常
                    throw new IllegalStateException("INVALID tlsResource");
                }
                count.incrementAndGet();
                return this.tlsResource.get();
            }           
        }else{
            return getResource();
        }
    }
    /**
     * (阻塞式)申请当前线程使用的资源对象,不可跨线程使用<br>
     * {@link InterruptedException}封装到{@link RuntimeException}抛出
     * @return
     * @see #applyChecked()
     */
    public final R apply(){
        try {
            return applyChecked();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 释放当前线程占用的资源对象,放回资源队列
     */
    public final void free(){
        if(nestable){
            AtomicInteger count = threadNestCount.get();
            if(null == count){
                // 申请/释放没有成对调用
                throw new IllegalStateException("INVALID nestCount");
            }
            if( 0 == count.decrementAndGet()){
                threadNestCount.remove();
                recycleResource();
            }           
        }else{
            recycleResource();
        }
    }
    /** 是否允许嵌套 */
    protected boolean isNestable() {
        return false;
    }
    /**
     * 创建一个新的资源对象
     * @return
     */
    protected R newResource(){
        return null;
    }
    /**
     * 资源从队形从取出时调用,子类可重写此方法
     * @param resource
     * @return 返回 {@code resource
     */
    protected R open(R resource){
        return resource;
    }
    /**
     * 资源对象放回队列时调用,子类可重写此方法
     * @param resource
     * @return 返回 {@code resource}
     */
    protected R close(R resource){
        return resource;
    }

完整代码参见gitee 代码仓库:ResourcePool.java

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏沉默王二

五分钟搞定 HTTPS 配置,二哥手把手教

FreeSSL.cn 是一个免费提供 HTTPS 证书申请、HTTPS 证书管理和 HTTPS 证书到期提醒服务的网站,旨在推进 HTTPS 证书的普及与应用,...

25250
来自专栏互扯程序

docker私有仓库搭建,证书认证,鉴权管理

-Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

29120
来自专栏颇忒脱的技术博客

X.509、PKCS文件格式介绍

也就是说ASN.1是一种用来定义数据结构的接口描述语言,它不是二进制,也不是文件格式,看下面的例子你就会明白了:

20110
来自专栏Android机动车

Android 10 新特性 率先看

设备位置:让用户能够控制应用程序何时可以获得位置信息,包括应用程序何时不使用位置信息。用户可以设置应用程序无法使用位置信息,可以选择只有应用程序在运行或者在后台...

21820
来自专栏程序员历小冰

作为程序员,必须知道的 Web 协议有哪些?

你会发现,这些问题其实都和 Web 协议密切相关。我常常听到身边人抱怨“学不动了”,之所以会这样,大多是因为没有掌握好互联网体系中的底层知识。这些知识点相对稳定...

26130
来自专栏Java技术栈

Web 协议的 7 个困惑,大佬带你全部解开!

你会发现,这些问题其实都和 Web 协议密切相关。我常常听到身边人抱怨“学不动了”,之所以会这样,大多是因为没有掌握好互联网体系中的底层知识。

14620
来自专栏玩转全栈

nginx如何代理多个express服务

背景是这样的,我目前有一台服务器,域名已经申请了brzhang.club,证书也申请了,可以看到是https的,安全访问无污染,哈哈!

48050
来自专栏技术探索

es集群使用xpack(基于elastic 6.5.4)

保存elastic-certificates.p12路径并输入密码(123456) 将上面生成的两个文件拷贝到elastic的config目录下 比如我设置的是...

1.2K30
来自专栏java思维导图

作为程序员,必须知道的 Web 协议有哪些?

你会发现,这些问题其实都和 Web 协议密切相关。我常常听到身边人抱怨“学不动了”,之所以会这样,大多是因为没有掌握好互联网体系中的底层知识。这些知识点相对稳定...

11030
来自专栏Web行业观察

【翻译】JS的回归: 设计一个包含CMS和CRM应用服务的node.js软件架构

Adults Use of Information and Communication Technologies in Healthcare (auICTH 2...

25720

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励