如果光标追上了垃圾回收器线程,那么会出现什么情况呢? 首先,环形缓冲区的大小都足够大,而且监听器运行地足够快,那么这种情况就不会发生。然而,如果这种情况出现,那么环形缓冲区将阻止光标前移到垃圾回收器(通常垃圾回收器都紧随在最慢的消费者之后),或者超过。因此,添加ChangeSet对象的方法将处于阻塞状态,直到光标可以前移为止。
光标从不会“覆盖”垃圾回收器或者消费者, 因此就会自然产生压力回传。
在实际运行的内容缓冲库中,回传压力就意味着要花稍长时间进行保存操作。不过,如果这种情况出现的次数比你想象的要频繁,那么你就可以选择增大缓冲区,并重新启动内容缓冲库。事实上,这也意味着你的系统没有足够多的CPU核心去处理这么多监听器,或者意味着一个或者多个监听器花费了太长时间,这也意味着你应当考虑使用JCR事件日志,而不是采用监听器框架。(通过使用事件日志,你编写的代码可以请求某段时间内所发生的变更。)
经过上面的详细讨论,似乎环形缓冲存在大量的潜在冲突。实际上,一个良好的环形缓冲实现是在不需要使用锁技术或者同步技术就能够保持其协调一致性的。我们的实现确实做到了这样:这样的实现使用了 volatile long和比较与交换(CAS)操作来追踪光标、消费者和垃圾回收器各自所在位置,同时这样的逻辑也保证了消费者从来不会超越光标位置。事实上,我们也使用了同样的技术和代码来保证光标不会覆盖或者超过垃圾回收器线程;毕竟,缓冲区是一个有限的环形缓冲区。
当所有的消费者都追赶上光标,而且不再有ChangeSet对象可添加的时候,我们的实现就会让每个消费者线程阻塞,直到有ChangeSet对象添加为止。这种情形下,我们仅仅使用了一个简单的Java锁条件就做到了这些;这样的Java锁条件从来不会阻止添加ChangeSet对象。
换个说法,环形缓冲区应该运行的非常快。我们研究了各种环形缓冲区的实现,其中包括LMAX Disruptor(它非常优秀)。Disruptor的大多数功能都非常的优秀,不过也有几个功能并不是很好,基于此我们很快自己编写了自己的实现。
使用LMAX Disruptor的ChangeBus实现与我们旧的实现相比速度快的就根本不再一个数量级上,然而使用我们编写的环形缓冲的实现的速度则更快些。由于我们的实现代码量小,而且集中解决我们所需,同时也不依赖第三方代码,因此我们决定把自己编写的代码优化的更强大,并集成到4.0代码库中。新的ChangeBus实现将会首先出现在在ModeShap 4.0.0.Alpha3版本里。
这篇文章有些冗长,希望你能从中找到你所感兴趣并且对你有帮助的东西。同时对一个ModeShape用户来说,也许你想更深入的了解ModeShape是如何处理事件的,其中的一种方法在ModeShape4中有了改进。