zoukankan      html  css  js  c++  java
  • Java并发编程实战 第11章 性能与可伸缩性

    关于性能

    性能的衡量标准有很多,如:

    服务时间,等待时间用来衡量程序的"运行速度""多快"。

    吞吐量,生产量用于衡量程序的"处理能力",能够完成"多少"工作。

    多快和多少有时候是互相矛盾的。

    关于可伸缩性

    可伸缩性:当增加计算资源(CPU 内存 存储 带宽)时,程序的吞吐量或者处理能力能够相应的增加。

    评估各种性能权衡的因素

    各种性能指标之间权衡,需要考虑的是系统需求和使用场景。

    例如,快速排序算法在处理大规模数据集上的执行效率较高,但是小规模数据可能更加适合冒泡排序。

    Amdahl定律

    定义:在无限增加计算资源的情况下,理论上系统能够达到性能提升比率(最高加速比),局限于程序中可并行组件与串行组件所占的比重。

    具体的公式就不列出来了,举个例子,如果系统中必须串行的部分占比为1/2,那么即使你把系统的CPU无限加大,最高也只能达到2倍的加速。如果系统中必须串行的部分占比为1/10,那么即使你把系统的CPU无限加大,最高也只能达到10倍的加速。

    Amdahl告诉我们,并不是无限的加大CPU就能够提高性能的。

    引入线程的开销

    • 上下文切换

    如果在运行的线程数大于CPU数目,那么一个线程的时间片结束,或者被阻塞,将它调出,把时间片交给另外一个线程。就会引起上下文切换。

    因为上下文切换带来的负载,操作系统调度器会为每个线程分配一个最小执行时间(如果分配的执行时间还没有上下文切换的时间多,那么岂不是时间都浪费在上下文切换上了)。

    一个线程发生阻塞,会放弃它的时间片。所以频繁阻塞会引起过多的上下文切换。

    上下文切换就是保存一个线程的状态,加载下一个线程的状态。

    • 内存同步

    synchronize和volatile带来的可视性保证可能会使用一些特殊指令,如内存栅栏,它可以刷新缓存,使缓存无效,刷新硬件的写缓存,以及停止执行管道,抑制编译器优化,如重排序。

    • 阻塞

    JVM在实现阻塞的时候,有两种方式:

    自旋等待,不断地轮询trylock。适用于阻塞时间较短的情况。

    使用操作系统挂起线程。适用于阻塞时间较长的情况。

    减少锁的竞争

    串行操作可以降低可伸缩性。在并行操作内部,可伸缩性最大的威胁就是锁的竞争。

    有两个因素可以影响锁的竞争:

    • 请求频率。
    • 锁定时间

    如果两者都很小,那么在锁上发生竞争的机会就比较少。

    有3中方式可以减少锁的竞争。

    • 锁定时间层面,快进快出

    做法就是分解拆分过长的同步代码块。

    • 降低线程请求锁的频率

    做法就是锁分解和锁分段。跟上面不一样,上面是分解同步代码块。

    锁分解:如果一个锁维护了多个相互独立的状态变量。那么可以使用多个锁来维护每个独立的状态变量。

    锁分段:使用锁分解技术进一步的将一组数据拆分,然后在拆分后的数据上建立多个锁。

    锁分段的案例:

    ConcurrentHashMap默认使用包含16个锁的数组,每个锁保护散列通的1/16。

    • 不使用独占锁

    如使用并发容器,读写锁,不可变对象,原子变量等。

  • 相关阅读:
    实习笔记day03
    实习笔记day02
    实习笔记day01
    第4章:数组与方法
    栈内存与堆内存的区别
    java数据类型
    保护模式指令
    空描述符
    段描述符
    全局描述符表
  • 原文地址:https://www.cnblogs.com/xiaolang8762400/p/7074161.html
Copyright © 2011-2022 走看看