2.偏向锁,轻量锁,重量锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.偏向锁,轻量锁,重量锁相关的知识,希望对你有一定的参考价值。

参考技术A 1.偏向锁,我们在保证线程安全的的情况下,实际情况下,不一定有互斥,如果锁对象没有其他竞争资源,则操作系统默认会为偏向锁,锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用他的线程拥有,偏向锁不会调用os 函数,main函数启动式,但是会存在延时加载,此段时间为轻量锁
2.轻量锁,偏向锁升级 JVM 启动式默认是偏向锁,---两个线程交替执行,无竞争
 轻量锁与偏向锁不同的是:
轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁
每次进入退出同步块都需要CAS更新对象头
争夺轻量级锁失败时,自旋尝试抢占锁
 可以看到轻量锁适合在竞争情况下使用,其自旋锁可以保证响应速度快,但自旋操作会占用CPU,所以一些计算时间长的操作不适合使用轻量级锁。

3.synchronized 是重量锁(mutex--互斥),借助OS函数实现的锁,当竞争线程尝试占用轻量级锁失败多次之后,轻量级锁就会膨胀为重量级锁,重量级线程指针指向竞争线程,竞争线程也会阻塞,等待轻量级线程释放锁后唤醒他。

一句话概括:没有资源竞争(偏向锁),有资源竞争(轻量锁),资源多次竞争失败,轻量锁升级(重量)

证明偏向锁
Linux 打印线程id 使用c打印线程id,在linux中打印只会打印一次;而重量锁则会交替打印

Java 并发编程线程锁机制 ( 锁的四种状态 | 无锁状态 | 偏向锁 | 轻量级锁 | 重量级锁 | 锁竞争 | 锁升级 )





一、悲观锁示例 ( ReentrantLock )



ReentrantLocksynchronized 都是悲观锁 ;

ReentrantLockLock 接口的实现类 ,

public class ReentrantLock implements Lock, java.io.Serializable {
}

Lock 是一种锁的机制 , 调用 lock() 方法 , 表示要对下方的代码进行加锁 , 这些代码是线程安全的 ;

代码执行完毕后 , 调用 unlock() 释放锁 ;

lock()unlock() 之间的内容 , 就是同步代码块内容 ;

public interface Lock {

    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

悲观锁都是重量级锁 ;





二、重量级锁弊端



JDK 1.2 1.2 1.2 之间 , 只有一个 synchronized 重量级锁 ;

Java 虚拟机创建了线程 A , B 两个线程 , JVM 将线程托管给操作系统进行调度执行 , 线程同步依靠 synchronized 重量级锁实现 , 线程 A , B 之间会进行竞争 , 哪个抢到 synchronized 锁 , 哪个线程就可以执行 ;

使用 synchronized 使用起来效率很低 , 假如在 synchronized 同步代码块中 , 只有一行代码 , 执行 1 1 1 ms , 但是系统调度线程 , 可能需要 20 20 20 ms 才能轮到线程执行 , 线程执行的时间远远小于调度时间 , 这样线程执行效率很低 ;

为了 Java 程序的提升执行效率 , Java 引入了 4 4 4 种锁状态 , 无锁 , 偏向锁 , 轻量级锁 , 重量级锁 ;





三、锁的四种状态 ( 无锁状态 | 偏向锁 | 轻量级锁 | 重量级锁 )



Java 虚拟机堆内存中的对象数据中 , 每个对象都有一个对象头 , 结构如下 :

对象头 中封装了 锁的状态 , 当锁的状态发生改变时 , 对应的锁的标志位也进行相应修改 ;


无锁状态 : 不进行加锁 , 线程不安全 ;

偏向锁 : 1 1 1 个访问 共享资源 的线程 A , 做一个标记 , 不加锁 , 这个标记称为 " 偏向锁 " ; 偏向锁 偏向第一个访问的线程 ; 如果没有新的线程竞争该锁 , 则该 偏向锁一直被该线程持有 , 不会释放锁 ; 如果出现多个线程同时访问 , 持有偏向锁的线程会 释放该偏向锁 , 并添加轻量级锁 ;

  • 锁竞争 : 多个线程尝试获取同一个锁 ;
    • 没有竞争 : 如果每次获取都很顺利 , 没有出现阻塞 , 则没有竞争 ;
    • 有竞争 : 如果线程尝试获取锁 , 但是锁被其它线程持有 , 那么 该线程需要等待 , 期间 阻塞或自旋 , 只要是等待就会产生消耗 , 这就产生了锁竞争 , 并且 有一定的性能消耗 ;
  • 锁竞争消耗 : 多数情况下锁会被多个线程获取多次 , 多个线程竞争一个锁 , 这样就存在竞争 , 竞争期间 阻塞或自旋 , 锁获取的代价很大 ;
  • 偏向锁优点 : 降低了线程获取锁的代价 , 偏向锁不存在锁竞争问题 ;
  • 偏向锁意义 : 偏向锁并 不是真正意义上的锁 , 只是给单线程执行加了层保险 , 如果没有线程竞争该锁 , 则正常执行 , 如果有线程竞争 , 则将偏向锁升级为轻量级锁 ;

轻量级锁 : 自旋锁 , 等待期间一直做自旋操作 , 效率较高 , 但是空耗 CPU 性能 ; 自旋就是 while / for 循环 ;

重量级锁 : 系统提供的 synchronized , ReentrantLock 等重量级锁 , 由操作系统进行调度 , 可进行阻塞 ;





四、锁的四种状态之间的转换 ( 无锁状态 -> 偏向锁 -> 轻量级锁 -> 重量级锁 )



锁的四种状态之间转换 : 在保证线程安全的前提下 , 尽可能提升效率 ;

  • 无锁 : 刚开始执行时 , 无锁 ;

  • 无锁 -> 偏向锁 : 1 1 1 个线程访问共享资源时 , 无锁状态升级为偏向锁 ;

  • 偏向锁 -> 轻量级锁 : 2 2 2 个线程再来访问 共享资源 时 , 偏向锁 升级为 轻量级锁 ;

  • 轻量级锁 -> 重量级锁 : 如果 自旋线程数 超过 CPU 核数一半 , 或 单个线程超过 10 10 10 次自旋 , 自动将锁升级为重量级锁 ;

以上是关于2.偏向锁,轻量锁,重量锁的主要内容,如果未能解决你的问题,请参考以下文章

自旋锁,偏向锁,轻量级锁,重量级锁

多线程synchronized 中的 锁优化的机制 (偏向锁-;轻量级锁-;重量级锁)

图解 偏向锁,轻量锁,重量锁

图解 偏向锁,轻量锁,重量锁

偏向锁,轻量级锁,重量级锁

Java 偏向锁轻量级锁和重量级锁