同时还有一些缺点:
当ChangeSet送达总线的时候,总线不得不依次向所有的监听器队列添加这个ChangeSet,而且在从添加方法返回之前,它一直在做添加这个动作。因此,总线拥有的监听器越多,添加这个动作花费的时间就会越长。
处于阻塞状态的队列都持有内部锁,在向阻塞队列添加ChangeSet之前必须获取到这把锁,同时消费者进程也参与获取这把锁的竞争。这就会延迟ChangeBus的添加操作。
要把最新的ChangeSet添加到最后一个监听器队列,只有等到把这个ChangeSet添加到所有其他监听器队列后才可进行。这样就会在ChangeBus接收到ChangeSet和添加这个ChangeSet到最后一个监听器之间产生时间差,添加ChangeSet的监听器越是靠后,这个时间差就会越来越大(这是因为这些监听器位于监听器列表的尾部)。
如果任意一个处于阻塞状态的队列已经处于满员状态(因为监听器处理ChangeSet s不够快引起的),那么ChangeBus的添加操作就会阻塞。这么做非常好(尤其是在会话状态发生变更的时候),因为它把压力回传给生产者进程,同时也要注意到:在把这个ChangeSet添加到后续队列之前添加操作一直处于阻塞状态。因此即便后续的监听器得以运行,它们也无法意识到新添加的ChangeSet,除非阻塞的那个监听器得以运行。因此添加到ChangeBus的任何一个监听器依赖位于其前的所有其他监听器。
每个监听器队列都会维护一份ChangeSet对象列表的拷贝,并且对这份拷贝进行了排序。监听器越多,队列就会越多。
注意:监听器数量越多,对性能的影响就会越大。我们知道3.x版本已经有相当大的时间延迟。而且在4.0的早期预发布版里,我们还在3.x的基础上增加了更多内部监听器,并且我们还计划给每个索引提供者增加更多监听器。
同时还有一些缺点:
当ChangeSet送达总线的时候,总线不得不依次向所有的监听器队列添加这个ChangeSet,而且在从添加方法返回之前,它一直在做添加这个动作。因此,总线拥有的监听器越多,添加这个动作花费的时间就会越长。
处于阻塞状态的队列都持有内部锁,在向阻塞队列添加ChangeSet之前必须获取到这把锁,同时消费者进程也参与获取这把锁的竞争。这就会延迟ChangeBus的添加操作。
要把最新的ChangeSet添加到最后一个监听器队列,只有等到把这个ChangeSet添加到所有其他监听器队列后才可进行。这样就会在ChangeBus接收到ChangeSet和添加这个ChangeSet到最后一个监听器之间产生时间差,添加ChangeSet的监听器越是靠后,这个时间差就会越来越大(这是因为这些监听器位于监听器列表的尾部)。
如果任意一个处于阻塞状态的队列已经处于满员状态(因为监听器处理ChangeSet s不够快引起的),那么ChangeBus的添加操作就会阻塞。这么做非常好(尤其是在会话状态发生变更的时候),因为它把压力回传给生产者进程,同时也要注意到:在把这个ChangeSet添加到后续队列之前添加操作一直处于阻塞状态。因此即便后续的监听器得以运行,它们也无法意识到新添加的ChangeSet,除非阻塞的那个监听器得以运行。因此添加到ChangeBus的任何一个监听器依赖位于其前的所有其他监听器。
每个监听器队列都会维护一份ChangeSet对象列表的拷贝,并且对这份拷贝进行了排序。监听器越多,队列就会越多。
注意:监听器数量越多,对性能的影响就会越大。我们知道3.x版本已经有相当大的时间延迟。而且在4.0的早期预发布版里,我们还在3.x的基础上增加了更多内部监听器,并且我们还计划给每个索引提供者增加更多监听器。
在上图中,数字代表的是缓冲中各项的位置,从1开始,逐次增加。光标的位置是7,读取ChangeSet的每个消费者线程处在各自不同的位置:6、4、3和2 。注意:紧跟在所有消费者线程后面的是垃圾回收线程,它只是对所有消费者线程都读取了的ChangSet引用清空。(我们需要这样的线程,因为环形缓冲通常只有1024或者2048个存储项,而且假设每个环形缓冲区都有具有许多更新的ChangSet的话,就会占用大量内存。环形缓冲的垃圾回收器可以通过JVM对已经处理完成的ChangeSet对象进行回收。)
下图是另一个环形缓冲图,这张图表示的是在另外添加了7个ChangeSet对象,并且监听器的消费者线程前移后的状态。
其中光标和所有的消费者线程以及垃圾回收器都已经前移。
每个消费者位置与其余消费者的位置是毫不相关的,然而,它们的位置很明显与可添加新变更项的光标位置相关。通常,监听器运行的非常快,以致于消费者都紧随在光标之后。当然,也存在其他情况,比如每个ChangeSet中包含的变更数量起伏很大的时候(通常都是这样的)。
添加的ChangeSet对象越多,光标前移的就会越快,这样就有可能抵达“环形缓冲开始“的地方,此时就会开始重用缓冲区中以前已经使用的缓冲项了。(实际上,缓冲区是一个简单的预先分配了固定大小的Object[],缓冲区的位置可以很容易地被认为是数组的索引。我们只是把它想象为一个环形缓冲区。)
光标最终将会重用不再需要的缓冲区中的各项。