温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

LimitLatch在Tomcat 中的应用是怎样的

发布时间:2021-12-08 18:02:57 来源:亿速云 阅读:119 作者:柒染 栏目:大数据

这篇文章给大家介绍LimitLatch在Tomcat 中的应用是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

Tomcat的LimitLatch类用于控制网络通信的socket接收上限,在Tomcat7时引入,实现简单,借此可以学习一下线程同步的相关知识。

LimitLatch依赖内部类Sync进行线程同步,而Sync继承自大家熟悉的AbstractQueuedSynchronizer。AQS是java.util.concurrent的核心组件,诸多常用的线程同步工具类都能够找到他的影子,读者可以翻阅ReentrantLock、CountDownLatch、Semaphore等类的源码。

//if we have reached max connections, wait

countUpOrAwaitConnection();

SocketChannel socket = null;

try {

      // Accept the next incoming connection from the server socket

     socket = serverSock.accept();

     ……

不管是NIO还是BIO,Tomcat在接收socket前,都要通过countUpOrAwaitConnection方法获取资源,如果已经达到最大连接数,则需要当前线程等待资源释放。该方法最终会调用到LimitLatch的内部类Sync的acquireSharedInterruptibly方法,即AQSacquireSharedInterruptibly方法。

从内部类Sync的重载方法我们能看到Sync是一个共享模式的同步器,重载了tryAcquireShared和tryReleaseShared两个方法,而两个方法之所以能够如此简单,就是因为父类AQS在背后默默完成了其他所有的排队、等待、激活等一系列逻辑。

protected int tryAcquireShared(int ignored) {           

 long newCount = count.incrementAndGet();        

    if (!released && newCount > limit) {//自增后没有超过资源上限则获取成功                // Limit exceeded               

       count.decrementAndGet();//资源获取失败,回退               

       return -1;        

    } else {

                return 1;      

      } 

 }

在获取共享资源时,LimitLatch.Sync使用了原子变量AtomicLong,利用其自增的CAS原子操作结果与设定的共享资源数量上限进行比较,如果超出上限则目前无法获取资源,由AQS放入等待队列等待下次触发。LimitLatch中定义了released属性,该属性为true时,无论如何都会获取到共享资源。

public boolean releaseAll() {     

   released = true;//标志位置为ture后,后续均可获取资源    

    return sync.releaseShared(0);//通知等待线程重新获取资源  

  }

这里就有一个问题了,既然无论如何都会获取到资源,LimitLatch就没有存在的必要,那为何还要这样一个看似多余的released 属性呢?这里其实考虑到一个状态变更的问题,当由一个LimitLatch控制资源获取量变更为无需LimitLatch时,仅仅将LimitLatch置为null从而跳过资源竞争是不够的。

如果之前存在在等待队列中等待资源的线程,而此时没有资源释放,那么在状态变更后线程仍然会处于等待状态,这与“无限制”的状态是不符的,此时需要将released属性置为true,然后通过一次资源释放由AQS触发所有等待线程重新获取资源,这个时候所有线程均会获取资源立即返回。

protected boolean tryReleaseShared(int arg) {   

         count.decrementAndGet();//自减释放资源     

       return true;    

    }

资源释放时的代码就更简单了,直接将代表资源的原子变量AtomicLong自减从而释放资源就完成了。而后续的唤醒等待资源的线程等工作已经由AQS代劳了。

写到这里,问题又来了,这个功能完全可以由JDK自带的Semaphore类来完成啊。如果非要再写一个那一定是因为性能的原因了,毕竟该类要使用在接收Socket的前面,对性能有直接影响。下面代码为Semaphore类(JDK1.8)的FairSync重写的tryAcquireShared方法,本质上与LimitLatch并无什么不同,都是CAS自旋:

protected int tryAcquireShared(int acquires) {            

for (;;) {              

 if (hasQueuedPredecessors())            

       return -1;              

 int available = getState();    

           int remaining = available - acquires;    

           if (remaining < 0 ||                    compareAndSetState(available, remaining))    

               return remaining;         

      }

   }

话不多说,开始性能测试,测试场景分为64线程竞争64个资源以及64线程竞争32个资源,循环300w次。测试结果竟然是LimitLatch性能要比Semaphore性能低近10%左右,这个。。。一定是我打开的方式不对。

关于LimitLatch在Tomcat 中的应用是怎样的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI