并发设计的思考
看了AtomicLong的实现或许会立马想到ReentrantLock或者Synchronized也可以实现原子类,只要在操作前获取锁,操作完释放锁。
但是为什么不用这些锁,而是用CAS呢?
显然,这些锁都是互斥锁,在多线程竞争激烈的情况下,伴随着大量线程上下文切换和独占,严重降低吞吐量。
然使用CAS + Volatile,这种乐观锁的机制,能使得线程Spin,并且可以并发读。这将会大幅度提升吞吐。
然而无论是互斥锁还是CAS + Volatile,它们本身都是一种锁机制。在严重竞争的情况下,都会有性能牺牲,比如大量的CAS失败,反复的Spin,消耗CPU资源。
在这种情况下,需要弄清楚竞争的资源是什么,是否可以将竞争点拆分,分摊压力。这种方式本质上是一种锁的粒度控制,将锁的粒度控制的更细,锁的范围更小。这种做法很多地方都有:
- ConcurrentHashMap中的实现,将对整个Map的锁,细化到对每个Segment上
- 如使用Synchronized或者ReentrantLock,尽量控制锁的范围,避免锁的范围
- Mysql的表锁和行锁设计,从MyISAM表锁到InnoDB支持行锁,将锁的粒度控制在行,从而提高存储引擎的并发
通过以上的优秀设计,可以总结出一套锁的优化思路:
互斥锁 -> 乐观锁 -> 锁的粒度控制
在Java中对应的实现方式:
ReentrantLock或者Syschronized -> CAS + Volatile -> 拆分竞争点