LimitLatch 在 Tomcat 中的应用

Stella981
• 阅读 599

本文的作者是我之前一起从事应用服务器研发的同事杨高强,高强现就职于某互联网巨头,功底深厚,技术牛X。以下为正文部分


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%左右,这个。。。一定是我打开的方式不对。

我们回顾一下,当前使用的环境为Windows,JDK版本为1.8,而tomcat7引入LimitLatch的时候正是JDK1.6横行的时代。笔者将JDK切换到1.6进行测试,果然结果变为两者不相上下;而后将测试代码上传到了服务器,在Linux、JDK1.6环境下重新测试,结果LimitLatch扭转乾坤,反超Semaphore性能20%左右,切换到JDK1.8性能领先Semaphore约40%!很明显Tomcat更注重的是Linux服务器下的性能,至于两者性能对比结果在不同环境下不同的原因,欢迎各位大牛一起讨论。

讲到这里,读者是不是对AQS的源码也非常感兴趣了呢,赶紧读起来吧?

☆★☆更多精彩内容☆★☆****

一台机器上安装多个Tomcat 的原理(回复001)

监控Tomcat中的各种数据 (回复002)

启动Tomcat的安全机制(回复003)

乱码问题的原理及解决方式(回复007)

Tomcat 日志工作原理及配置(回复011)

web.xml 解析实现(回复 012)

线程池的原理( 回复 014)

Tomcat 的集群搭建原理与实现 (回复 015)

类加载器的原理 (回复 016)

类找不到等问题 (回复 017)

代码的热替换实现(回复 018)

Tomcat 进程自动退出问题 (回复 019)

为什么总是返回404? (回复 020)

...

         iOS赞赏码!

LimitLatch  在 Tomcat 中的应用

PS: 对于一些 Tomcat常见问题,在公众号的【常见问题】菜单中,有需要的朋友欢迎关注查看。

觉得本文对你有帮助?请分享给更多人支持一下吧,谢谢****

关注『 Tomcat那些事儿  』 ,发现更多精彩文章!了解各种常见问题背后的原理与答案。深入源码,分析细节,内容原创,欢迎关注。

LimitLatch  在 Tomcat 中的应用

本文分享自微信公众号 - Tomcat那些事儿(tomcat0000)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这