Tomcat的LimitLatch类用于控制网络通信的socket接收上限。Tomcat7中引入,实现简单。通过这种方式,您可以了解线程同步。LimitLatch依赖内部类Sync进行线程同步,Sync继承自大家熟悉的AbstractQueuedSynchronizer。AQS是java.util.concurrent的核心组件。很多常用的线程同步工具类都能找到它的影子。读者可以阅读ReentrantLock、CountDownLatch、Semaphore等类的源码。//ifwehavereachedmaxconnections,waitcountUpOrAwaitConnection();SocketChannelsocket=null;try{//Acceptthenextincomingconnectionfromtheserversocketsocket=serverSock.accept();...无论是NIO还是BIO,Tomcat在接收socket之前都必须通过countUpOrAwaitConnection方法获取资源,如果是已达到***连接数要求当前线程等待资源释放。该方法最终会调用LimitLatch的内部类Sync的acquireSharedInterruptibly方法,即AQS的acquireSharedInterruptibly方法。从内部类Sync的重载方法可以看出,Sync是一个共享模式的同步器,它重载了tryAcquireShared和tryReleaseShared这两个方法,而这两个方法之所以这么简单,是父类AQS在背后默默完成的场景中剔除了排队、等待、激活等一系列其他逻辑。protectedinttryAcquireShared(intignored){longnewCount=count.incrementAndGet();if(!released&&newCount>limit){//自增不超过资源限制后,获取成功//limitexceededcount.decrementAndGet();//资源获取失败,fallbackreturn-1;}else{return1;}}获取共享资源时,LimitLatch.Sync使用原子变量AtomicLong,使用其自增的CAS原子操作结果与设置的共享资源数量上限进行比较,如果超过上限,则当前资源无法获取,AQS将其放入等待队列等待下一次触发。released属性在LimitLatch中定义。当该属性为真时,无论如何都会获取共享资源。publicbooleanreleaseAll(){released=true;//标志位置为真后,可以获取后续资源returnssync.releaseShared(0);//通知等待线程重新获取资源}这里有个问题,因为会反正得到了Resources,LimitLatch又不是必须的,为什么还要这么一个看似多余的release属性呢?其实这里考虑的是状态变化问题。当一个LimitLatch控制的资源获取量改为无LimitLatch时,仅仅将LimitLatch设置为null来跳过资源竞争是不够的。如果之前等待队列中有一个线程在等待资源,此时没有资源释放,状态改变后该线程仍处于等待状态,与“受限”状态不一致。这时,released属性被设置为true,然后AQS触发所有等待线程通过一次资源释放重新获取资源。这时候所有的线程都会获取到资源并立即返回。protectedbooleantryReleaseShared(intarg){count.decrementAndGet();//递减释放资源returntrue;}资源释放的代码更简单,直接递减代表资源的原子变量AtomicLong来释放资源。后续唤醒等待资源的线程等任务都由AQS完成。写到这里,问题又来了,这个功能可以通过JDK自带的Semaphore类来完成。如果非要再写一个,那肯定是性能方面的原因。毕竟这个类应该在接收Socket之前使用,对性能有直接的影响。下面的代码是Semaphore类(JDK1.8)的FairSync重写的tryAcquireShared方法,本质上和LimitLatch是一样的,都是CAS自旋:protectedinttryAcquireShared(intacquires){for(;;){if(hasQueuedPredecessors())return-1;intavailable=getState();intremaining=available-acquires;if(remaining<0||compareAndSetState(available,remaining))returnremaining;}}话不多说,开始性能测试,测试场景分进入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服务器下的性能。至于两者在不同环境下的性能对比结果为何不同,欢迎各位高手共同探讨。【本文为专栏作家“侯书城”原创稿件,转载请通过作者微信公众号“Tomcat物语”获得授权】点此查看本作者更多好文
