使用线程的一种说法是为了提升性能。多线程可使程序充分利用闲置的资源,提升资源的利用率,同时可以并行处理任务,提升系统的响应性。 可是很显然,引入线程的同时也引入了系统的复杂性。另外系统的性能并非老是随着线程数的增长而老是提升。算法
性能的提高一般意味着能够用更少的资源作更多的事情。这里资源是包括咱们常说的CPU周期、内存、网络带宽、磁盘IO、数据库、WEB服务等等。 引入多线程能够充分利用多核的优点,充分利用IO阻塞带来的延迟,也能够下降网络开销带来的影响,从而提升单位时间内的响应效率。数据库
为了提升性能,须要有效的利用咱们现有的处理资源,同时也要开拓新的可用资源。例如,对于CPU而言,理想情况下但愿CPU可以满负荷工做。固然这里满负荷工做是指作有用的事情,而不是无谓的死循环或者等待。受限于CPU的计算能力,若是CPU达到了极限,那么很显然咱们充分利用了计算能力。对于IO而言(内存、磁盘、网络等),若是达到了其对于的带宽,这些资源的利用率也就上去了。理想情况下全部资源的能力都被用完了,那么这个系统的性能达到了最大值。缓存
为了衡量系统的性能,有一些指标用于定性、定量的分析。例如服务时间、等待时间、吞吐量、效率、可伸缩性、生成量等等。服务时间、等待时间等用于衡量系统的效率,即到底有多快。吞吐量、生成量等用于衡量系统的容量,即可以处理多少数据。除此以外,有效服务时间、中断时间等用于能力系统的可靠性和稳定性等。网络
可伸缩性的意思是指增长计算资源,吞吐量和生产量相应获得的改进。 从算法的角度讲,一般用复杂度来衡量其对应的性能。例如时间复杂度、空间复杂度等。多线程
并行的任务增长资源显然可以提升性能,可是若是是串行的任务,增长资源并不必定可以获得合理的性能提高。 Amdahl定律描述的在一个系统中,增长处理器资源对系统行的提高比率。 假定在一个系统中,F是必须串行化执行的比重,N是处理器资源,那么随着N的增长最多增长的加速比:架构
理论上,当N趋近于无穷大时,加速比最大值无限趋近于1/F。 这意味着若是一个程序的串行化比重为50%,那么并行化后最大加速比为2倍。并发
加速比除了能够用于加速的比率外,也能够用于衡量CPU资源的利用率。若是每个CPU的资源利用率为100%,那么CPU的资源每次翻倍时,加速比也应该翻倍。 事实上,在拥有10个处理器的系统中,程序若是有10%是串行化的,那么最多能够加速1/(0.1+(1-0.1)/10)=5.3倍,换句话说CPU的利用率只用5.3/10=53%。而若是处理器增长到100倍,那么加速比为9.2倍,也就是说CPU的利用率只有个9.3%。性能
显然增长CPU的数量并不能提升CPU的利用率。下图描述的是随着CPU的数量增长,不一样串行化比重的系统的加速比。优化
很显然,串行比重越大,增长CPU资源的效果越不明显。操作系统
性能的提高能够从如下几个方面入手。
一个程序对系统平台的资源利用率是指某一个设备繁忙且服务于此程序的时间占全部时间的比率。从物理学的角度讲相似于有用功的比率。简单的说就是:资源利用率=有效繁忙时间/总耗费时间。
也就说尽量的让设备作有用的功,同时榨取其最大值。无用的循环可能会致使CPU 100%的使用率,但不必定是有效的工做。有效性一般难以衡量,一般只能以主观来评估,或者经过被优化的程序的行为来判断是否提升了有效性。
延迟描述的是完成任务所耗费的时间。延迟有时候也成为响应时间。若是有多个并行的操做,那么延迟取决于耗费时间最大的任务。
多处理是指在单一系统上同时执行多个进程或者多个程序的能力。多处理能力的好处是能够提升吞吐量。多处理能够有效利用多核CPU的资源。
多线程描述的是同一个地址空间内同时执行多个线程的过程。这些线程都有不一样的执行路径和不一样的栈结构。咱们说的并发性更多的是指针对线程。
同时执行多个程序或者任务称之为并发。单程序内的多任务处理或者多程序间的多任务处理都认为是并发。
吞吐量衡量系统在单位之间内能够完成的工做总量。对于硬件系统而言,吞吐量是物理介质的上限。在没有达到物理介质以前,提升系统的吞吐量也能够大幅度改进性能。同时吞吐量也是衡量性能的一个指标。
程序运行过程当中性能最差的地方。一般而言,串行的IO、磁盘IO、内存单元分配、网络IO等均可能形成瓶颈。某些使用太频繁的算法也有可能成为瓶颈。
这里的可扩展性主要是指程序或系统经过增长可以使用的资源而增长性能的能力。
假设引入的多线程都用于计算,那么性能必定会有很大的提高么? 其实引入多线程之后也会引入更多的开销。
若是可运行的线程数大于CPU的内核数,那么OS会根据必定的调度算法,强行切换正在运行的线程,从而使其它线程可以使用CPU周期。
切换线程会致使上下文切换。线程的调度会致使CPU须要在操做系统和进程间花费更多的时间片断,这样真正执行应用程序的时间就减小了。另外上下文切换也会致使缓存的频繁进出,对于一个刚被切换的线程来讲,可能因为高速缓冲中没有数据而变得更慢,从而致使更多的IO开销。
不一样线程间要进行数据同步,synchronized以及volatile提供的可见性都会致使缓存失效。线程栈之间的数据要和主存进行同步,这些同步有一些小小的开销。若是线程间同时要进行数据同步,那么这些同步的线程可能都会受阻。
当发生锁竞争时,失败的线程会致使阻塞。一般阻塞的线程可能在JVM内部进行自旋等待,或者被操做系统挂起。自旋等待可能会致使更多的CPU切片浪费,而操做系统挂起则会致使更多的上下文切换。
了解了性能的提高的几个方面,也了解性能的开销后,应用程序就要根据实际的场景进行取舍和评估。没有一劳永逸的优化方案,不断的进行小范围改进和调整是提升性能的有效手段。当前一些大的架构调整也会致使较大的性能的提高。
简单的原则是在保证逻辑正确的状况小,找到性能瓶颈,小步改进和优化。