NioEventLoop里面使用了MpscLinkedQueue作为taskQueue,替换了父类中默认的LinkedBlockingQueue队列。taskQueue主要用于存放可执行任务,其调用的频率非常高,因此使用一个更高效的队列能带来很大的收益。 为什么在NioEvnetLoop里用MpscLinkedQueue替换了LinkedBlockingQueue,是使用了更好的算法?还是通过舍弃一些功能的情况来达到更高效的目的?
我们知道LinkedBlockingQueue也是一个高效的线程安全的队列,它使用了takeLock和putLock两个锁分别作用与消费线程和生产线程,避免了消费者和生产者直接的竞争。然而在消费者之间、生产者之间依然需要竞争各自端的锁。 对于NioEventLoop来说,taskQueue只有一个消费者,即运行NioEventLoop.run()的那个线程(如果不理解这句话,可以看前一篇NioEventLoop的介绍)。也就是说消费端不需要锁, 那是不是我们去掉消费端那把锁就能提高效率了呢? 并不是!! 在只有一个消费者的情况下,获取锁的时候由于没有竞争,一个cas就能完成锁定,效率实极高的。因此只去掉消费端的锁,对于单消费者的场景是没有效率的提高的。 那么? 难道要把生产者的锁也去掉?!! netty可以告诉你,是的。去掉takeLock,去掉puLock,让效率飞一会。。。还有一个关键是,代码简单的你想哭!!!
好了,我们来看看MpscLinkedQueue是怎么在单消费者多生产者的场景下去锁的。
首先,我决定把MpscLinkedQueue类的注释翻译一下,方便你有一个初步的了解,防止你没看完直接跑去实践结果发现有问题。
1、一个无锁的支持单消费者多生产者的并发队列;
2、 允许多个生产者同时进行以下操作:offer(Object),add(Object),addAll(Collection);
3、只允许一个消费者进行以下操作:poll(),remove(),remove(Object),clear();
4、以下方法不支持:remove(Object o),removeAll(Collection),retainAll(Collection);为啥不支持这三个方法? 看了后面的源码分析,也许你就知道答案了。
首先MpscLinkedQueue里面有一堆莫名其妙的字段,这个是用来消除伪共享的(想了解伪共享,可以看看