Tomcat的环形队列SynchronizedQueue源码分析

Tomcat作为一个Java中流行高并发Web容器,使用了非常多的异步处理措施,其中就包括消费者-生产者模型

在默认的nio协议中,使用Accept线程接受用户请求,并将请求封装成Event提交到队列中,Poller线程从队列中消费数据,处理用户的请求

在这里插入图片描述

本次我们就来学习一下这种解耦神器-队列, Tomcat没有使用Java juc包提供的队列,而是自己写了一个:SynchronizedQueue,至于原因,官方的描述是这样的:

这主要是作为java.util.concurrent的无GC替代方案。ConcurrentLinkedQueue,当需要创建一个无限制的队列而不需要收缩队列时。其目的是以最少的垃圾尽快提供所需功能的最低限度。

核心属性

    //用数组来存储元素
    private Object[] queue;

    //队列大小
    private int size;

    //插入索引(可以理解为队尾)
    private int insert = 0;

    //移除索引(可以理解为队头)
    private int remove = 0;

核心方法

初始化
    public SynchronizedQueue(int initialSize) {
        //初始化数组大小
        queue = new Object[initialSize];
        size = initialSize;
    }
入队:offer
public synchronized boolean offer(T t) {
        queue[insert++] = t;

        // 当数组写满当时候,写指针指向数组第一个元素以达到循环利用的目的
        // tip:这里不用担心,下次写的时候会覆盖以前的元素。因为队列遵循先进先出的原则,
        // 如果队列有移除过一个元素的话,0号元素已经出队列了,覆盖也就没什么影响。
        // 如果没有移除过元素的话remove也是=0的,所以就会执行下面的扩容操作
        if (insert == size) {
            insert = 0;
        }
        
        //写指针和移除指针重合,表示已经没有位置写了,就该扩容
        if (insert == remove) {
            expand();
        }
        return true;
    }
扩容:expand
private void expand() {
        int newSize = size * 2;
        //建立一个新数组:大小是原来的两倍
        Object[] newQueue = new Object[newSize];

        //这里采用了一个native的方法来实现数据拷贝,别问为什么,问就是因为高效
        //此处拷贝的流程是这样的,假设有这样一个长度为8的数组:
        //    索引: 0 1 2 3 4 5 6 7
        //    元素: J A V A W U D I 
        //此时的insert=1,remove=1,队头元素是A,队尾元素是J
        //拷贝到新数组应该是这样的
        //    索引: 0 1 2 3 4 5 6 7 8 9 10 11 12 14 15 16
        //    元素: A V A W U D I J     
        
        // 因为要保持先进先出的原则,所以拷贝的时候先拷贝:A V A W U D I 
        System.arraycopy(queue, insert, newQueue, 0, size - insert);
        
        //再拷贝:J,因为J是最后进队列的,是队尾
        System.arraycopy(queue, 0, newQueue, size - insert, insert);
        
        //不理解的同学:请注意扩容时机,数组是完全满了之后再扩容的,结合这个再去看看上面的逻辑
        insert = size;
        remove = 0;
        queue = newQueue;
        size = newSize;
    }
出队:poll
public synchronized T poll() {
        //写指针和移除指针相等,表示队列还是空的。
        // 因为元素插入之后,在offer里面进行判断处理:insert==remove就会扩容,
        //扩容之后insert肯定不会等于remove
        // 当所有元素的都移除之后,insert就会重新等于remove
        if (insert == remove) {
            // empty
            return null;
        }

        @SuppressWarnings("unchecked")
        T result = (T) queue[remove];
        queue[remove] = null;
        remove++;

        //数组最后一个元素移除完了,下次该移除数组第一个元素了
        if (remove == size) {
            remove = 0;
        }

        return result;
    }
总结

SynchronizedQueue作为一个环形队列,除了在空间利用率上很高效,同时还是线程安全,在核心方法offer、poll、size、clear都使用了synchronized关键字来保证在多线程环境下的安全。

学到了吧,赶紧用起来,下回项目中要用什么队列的时候,就把这复制过去,记得改成自己的专属名字 ~~~~~~~hhh学到就装到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值