一、性能和思考
前言:使用并发的目的是要提高性能,引入多线程之后,其实会带来其他额外开销,如线程间的协作、增加的上下文的切换、线程的创建和销毁、线程的调度等等。过度的或对多线程的使用不当会导致多线程甚至没有多线程的效率高。
衡量应用程序性能的标准:
服务时间、延迟时间(多块)、吞吐量(处理能力的指标、完成工作的多少)、可伸缩性等。对于服务器应用程序:可伸缩性、吞吐量这个方面往往是比较看重的。
开发应用时的思路:
- 先确保程序正确性、确实达不到期望的效果时,再提高速度,切勿一开始就想设计出完美的程序或者速度很快的程序。
- 要以实际测试数据为基准
1、影响性能的因素
-
上下文的切换
是指CPU 从一个进程或线程切换到另一个进程或线程。一次上下文切换花费5000~10000个时钟周期,几微秒。在上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行。从这个角度来看,上下文切换有点像我们同时阅读几本书,在来回切换书本的同时我们需要记住每本书当前读到的页码。
上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。 -
内存同步
一般指加锁,对加锁来说,需要增加额外的指令,这些指令都需要刷新缓存等等操作。 -
阻塞
会导致线程挂起【挂起:挂起进程在操作系统中可以定义为暂时被淘汰出内存的进程,机器的资源是有限的,在资源不足的情况下,操作系统对在内存中的程序进行合理的安排,其中有的进程被暂时调离出内存,当条件允许的时候,会被操作系统再次调回内存,重新进入等待被执行的状态即就绪态,系统在超过一定的时间没有任何动作】。很明显这个操作包括两次额外的上下文切换
2、避免性能问题
- 减少锁的竞争
类似于活锁这样状态,避免线程对锁的过于活跃的竞争且拿不到。 - 减少锁的粒度
使用锁的时候,锁所保护的对象是多个,当这些多个对象其实是独立变化的时候,不如用多个锁来一一保护这些对象。但是如果有同时要持有多个锁的业务方法,要注意避免发生死锁 - 缩小锁的范围
对锁的持有实现快进快出,尽量缩短持由锁的的时间。将一些与锁无关的代码移出锁的范围,特别是一些耗时,可能阻塞的操作 - 锁分段
类似于concurrentHashMap的Segment操作,可提升并发性能 - 替换独占锁
在业务允许的情况下:
1、使用读写锁,
2、用自旋CAS
3、使用系统的并发容器
二、线程安全的单例模式
这里介绍我的另一片文章:线程安全的单例模式