Java高性能线程通讯

目录

今天在infoq的网站上看到一篇有趣的文章Java里快如闪电的线程间通讯。文章探索的是多个生产者、单一消费者场景下高效无锁并发处理模型,看了作者的几种解决问题的办法,代码在github上https://github.com/asyncj/core

  1. 首先,很多情况下,写的糟糕的多线程代码并不如单线程代码高效!
  2. 作者的方案一,阻塞队列 + 缓存线程池,根据作者的测算,大概是单线程代码throughput的五千分之一。呃,这是最容易想到,也是最常用的办法,其效率也是显而易见的……不过需要说明的是,这里的case是纯CPU敏感的操作,如果是涉及IO操作的话,这种方案和单线程处理相较的话,我觉得差距没有那么大甚至要优于单线程。
  3. 方案二,作者参照了无锁算法实现,将阻塞队列改造成为了无锁队列,不过很遗憾,并没有带来作者心目中的提升。
  4. 方案三,作者使用了LMAX Disruptor,一个基于ring buffer的高性能进程间通信框架,它的多个生产者和消费者可以互相不阻塞的生产、消费消息,使用disruptor的demo在这里。作者测试的性能还不错,是阻塞队列性能的2倍,不过还达不到他的期望╮(╯▽╰)╭
  5. 方案四,作者设计的train model。首先,利用取模操作让多个生产者之间(循环处理每个生产者的业务)、生产者和消费者之间(生产者和消费者在不同thread、不同的station)避免了竞争,免去了同步的操作;然后,利用stationIndex的原子操作,避免了生产者和消费者之间的锁操作。然后无锁的设计是一方面,实现高性能的关键点在于waitTrainOnStation方法中的yield操作。在学习python的时候,就被coroutine的性能震精过,现在在java里又看到了yield,上来就是满满的好感啊。不过这个貌似和python中的yield含义略有不同。java中的yield会把当前的thread状态由运行态改为就绪态,如果这时候有优先级大于或者等于当前线程的任务在排队,它就可以得到运行的机会,因此如果大家的优先级相同,当前线程也是有机会在yield之后继续运行的。相较于sleep,在CPU敏感的操作中,yield会让CPU得到更高的利用率。比如在作者的Case中,使用yield操作可以观察到CPU的利用率瞬间冲到并保持在100%,而把它改为Thread.sleep(1)之后,CPU的利用率就只有不到20%左右了,两者的throughput就很好比较了,几乎差了一个数量级。

所以,总结起来作者的经验就是两点,良好设计的无锁队列+使用yield替代sleep操作。作为一个低端屌丝,今天才了解到yield的用法,真是相见恨晚,作者文中提到的高性能进程间通讯,其实主要是高性能进程间切换的意思啊。