11.1 对性能的思考
11.1.1 性能与可伸缩性
可伸缩性:当增加计算资源时(例如CPU、内存、存储容量或I/O带宽),程序的吞吐量或者处理能力能相应地增加。
对于服务器应用程序来说,“多少”这个方面——可伸缩性、吞吐量和生产量,往往比“多快”这个方面更受重视。
11.1.2 评估各种性能权衡因素
避免不成熟的优化,首先使程序正确,然后再提高运行速度——如果它运行得不够快。
以测试为基准,不要猜测。
11.2 Amdahl定律
image.png
N是处理器数,F是串行部分占比。
所有并发程序中都包含一些串行部分。
11.2.1 示例:在各种框架中隐藏的串行部分
11.2.2 Amdahl定律的应用
11.3 线程引入的开销
上下文切换的开销(11.3.1)和同步本身的开销(11.3.2),非竞争同步开销比较小,需要关注竞争同步。
竞争同步会引起阻塞(11.3.3),要么是用自旋锁,要么是挂起(大多数JVM),自旋锁适合等待时间短,反之适合挂起。挂起依赖操作系统,并且会引入两次上下文切换和必要的操作系统操作及缓存操作。
11.4 减少锁的竞争
3种方式降低锁的竞争程度:
减少锁的持有时间;
降低锁的请求频率;
使用带有协调机制的独占锁,这些机制允许更高的并发性。
11.4.1谈了缩小锁的范围(快进快出),这应该是用了第1种方式。
11.4.2谈了把一个锁拆分成两个,相对于一把锁,降低了请求频率,第2种方式。
11.4.3更进一步,把锁拆成多个,举例:ConcurrentHashMap,16个分段锁。
11.4.4谈了热点域的问题,当所有操作都对一个地方加锁时就形成了热点,要避免热点带了的可伸缩性问题。
11.4.5讲一些替代独占锁的方法,比如读写锁和源自变量(可以用在11.4.4提到的热点域问题),这是方式3。
11.4.6将如何检测CPU的利用率。
11.4.7专门吐槽对象池这一历史产物。
11.5示例:比较Map的性能
11.6减少上下文切换的开销