1.锁的升级
synchronized在JDK1.6的时候做了比较大的改动,在之后的JDK版本中,synchronized存在偏向锁、轻量级锁、重量级锁三种状态
1.1.偏向锁
线程之间不存在竞争行为,在同一时刻至多只有一个线程有获取锁的需求,常见常见为单线程场景
- 偏向锁的识别方法
- 判断是不是偏向锁的标识是查看调用方法的线程是否只有一个。在多线程编程中,被锁修饰的代码块仅对单个线程访问几乎是不存在的,因此偏向锁鸡肋(但是它也有它的优势之处,比如偏向锁对偏向线程是不会再有加锁的动作,从而就节省了CAS替换对象头中Mark Word中偏向线程ID的操作,进而提升了锁的性能)如果能够明确单一线程调用同步方法,使用无锁编程更为合适
- 性能比较
- 无锁和偏向锁的性能差异非常接近,几乎可以忽略不计
1.2.轻量级锁
线程之间存在锁的伪竞争行为,即在同一时刻绝对不存在两个及以上线程同时申请同一把锁,各个线程尽管都有使用锁的需求,但是是交替(交错)使用
- 轻量级锁的识别方法
- 当两个及两个以上线程不在同一时刻调用被锁修饰的方法,那么至少可以确定是轻量级锁
- 性能比较
- 轻量级锁由于同一时刻不存在两个及两个以上线程互相竞锁,因此不存在线程堵塞-唤醒的上下文切换,性能相对重量级锁要高很多
1.3.重量级锁
线程之间存在实质性的竞争行为,线程之间都有获取锁的需求,但是时间不可交错,互斥锁的堵塞等待
- 重量级锁的识别方法
- 当能够肯定至少两个及以上线程调用锁修饰的方法时,线程调用方法的顺序是随机的,那么大概率就是重量级锁
- 性能比较
- 重量级锁由于涉及到线程堵塞-唤醒上下文切换,造成的性能损耗相对无锁态要低很多
2.锁的优缺点
锁的类型 | 优点 | 缺点 | 使用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距 | 如果线程之间存在锁竞争,会带来额外的锁的消耗 | 适用于单线程访问同步块场景 |
轻量级锁 | 竞争的锁不会堵塞,提高了程序的响应速度 | 如果始终得不到锁的竞争线程,使用自旋尝试获取锁将会消耗CPU,导致CPU空转 | 追求响应速度,同步块执行速度非常快 |
重量级锁 | 线程竞争不会使用自旋,不会消耗CPU | 线程堵塞,响应时间缓慢 | 追求吞吐量,同步块执行时间较长 |