V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
fangbei
V2EX  ›  问与答

如何提高 Java 并行程序性能?

  •  
  •   fangbei · 2015-11-24 17:04:01 +08:00 · 1652 次点击
    这是一个创建于 3095 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在 Java 程序中,多线程几乎已经无处不在。与单线程相比,多线程程序的设计和实现略微困难,但通过多线程,我们却可以获得多核 CPU 带来的性能飞跃,从这个角度说,多线程是一种值得尝试的技术。那么如何写出高效的多线程程序呢?
    1 、有关多线程的误区:线程越多,性能越好
    不少初学者可能认为,线程数量越多,那么性能应该越好。因为程序给我们的直观感受总是这样。一个两个线程可能跑的很难,线程一多可能就快了。但事实并非如此。因为一个物理 CPU 一次只能执行一个线程,多个线程则意味着必须进行线程的上下文切换,而这个代价是很高的。因此,线程数量必须适量,最好的情况应该是 N 个 CPU 使用 N 个线程,并且让每个 CPU 的占有率都达到 100%,这种情况下,系统的吞吐量才发挥到极致。但现实中,不太可能让单线程独占 CPU 达到 100%,一个普遍的愿意是因为 IO 操作,无论是磁盘 IO 还是网络 IO 都是很慢的。线程在执行中会等待,因此效率就下来了。这也就是为什么在一个物理核上执行多个线程会感觉效率高了,对于程序调度来说,一个线程等待时,也正是其它线程执行的大好机会,因此, CPU 资源得到了充分的利用。

    2 、尽可能不要挂起线程
    多线程程序免不了要同步,最直接的方法就是使用锁。每次只允许一个线程进入临界区,让其它相关线程等待。等待有 2 种,一种是直接使用操作系统指令挂起线程,另外一种是自旋等待。在操作系统直接挂起,是一种简单粗暴的实现,性能较差,不太适用于高并发的场景,因为随之而来的问题就是大量的线程上下文切换。如果可以,尝试一下进行有限的自旋等待,等待不成功再去挂起线程也不迟。这样很有可能可以避免一些无谓的开销。 JDK 中 ConcurrentHashMap 的实现里就有一些自旋等待的实现。此外 Java 虚拟机层面,对 synchronized 关键字也有自旋等待的优化。

    3 、善用“无锁”
    阻塞线程会带来性能开销,因此,一种提供性能的方案就是使用无锁的 CAS 操作。 JDK 中的原子类,如 AtomicInteger 正是使用了这种方案。在高并发环境中,冲突较多的情况下,它们的性能远远好于传统的锁操作(《实战 Java 高并发程序设计》 P158 )。

    4 、处理好“伪共享”问题
    大家知道, CPU 有一个高速缓存 Cache 。在 Cache 中,读写数据的最小单位是缓存行,如果 2 个变量存在一个缓存行中,那么在多线程访问中,可能会相互影响彼此的性能。因此将变量存放于独立的缓存行中,也有助于变量在多线程访问是的性能提升(《实战 Java 高并发程序设计》 P200 ),大量的高并发库都会采用这种技术。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1095 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 18:52 · PVG 02:52 · LAX 11:52 · JFK 14:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.