一、锁的分类及优化
- 悲观锁
- 乐观锁
- 偏向锁
- 自旋锁 (轻量级锁)
- 重入锁
锁的优化:无锁→偏向锁→(轻量级锁)自旋锁→重量锁
1. 悲观锁:(synchronize锁就是悲观锁的体现)
1)简述:
每次对数据的操作,都会担心数据被修改,每次操作时要对数据进行加锁,只有获取锁的线程才能操作该数据,操作该数据其他线程就会被阻塞
2)使用场景:写多读少
3)缺点:悲观锁的使用需要不断加锁,释放锁。这样会引起线程的上下文切换和延时调度,会影响系统性能, 未获取锁的线程会被阻塞住。
2.乐观锁:(CAS就是乐观锁的体现)CAS详情! 请点击…
1)简述
每次对数据的操作,都不会担心数据被修改,对数据进行读取操作不需要加锁
当更新数据时需要判断数据是否被修改,未被修改时则可以直接更新
2)使用场景:读多写少
3.偏向锁:
偏向锁的操作实际上没有去找操作系统。
每一个对象都有属于自己的对象头,其中有一个Mark Word当中有几个标识位和其他数据,如下图
锁的相关信息记录在对象头中
JVM使用CAS操作把线程ID记录到了Mark Word的数据中,修改了偏向和标识位,当前记录的线程就拥有了这把锁
从上图看出:JVM不用操作系统的介入,只记录线程ID,就能让当前线程拥有锁
当线程拥有的锁,就可以执行synchronized修饰的代码块,当线程再一次执行到Synchronized的时候,JVM通过Mark word判断,当前 线程ID还在,并且还持有这个对象的锁,就可以继续进入临界区执行代码。在没有别的线程竞争的时候,锁一直偏向这个线程,当前线程可以一直执行下去。
4.自旋锁(轻量级锁):
JVM把对象恢复成无锁状态,在当前两线程的栈帧中各自分配了一个空间,叫做Lock Record,把锁对象的Mark word在两个线程的栈帧中各自复制了一份,叫做Displaced Mark,然后当前线程的 Lock Record的地址使用CAS放到了 Mark word中,并把锁标志位改成了00,这表示当前线程已经获得了这个轻量级锁,可以进入临界区执行。
若线程没有获得锁,JVM会让它自旋几次,等待一会,等当前退出临界区,释放锁的时候,需要把**Displaced Mard word **使用CAS复制回去,然后就可以加锁了。
两个线程交替进入临界区执行这一段代码,很少会出现竞争。
5.重入锁:ReenTrantLock JDK剖析可重入锁详情,请点击…
- 特征: 可中断响应,锁限时操作,公平锁,结合Condition使用
- 使用:
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
try{
//操作
}finally{
lock.unlock();
}