Java并发 sychronized 关键字

主要资料参考于 JavaGuide https://github.com/Snailclimb/JavaGuide#java,再加上自己的理解

JDK 1.6 之后对锁的实现进行了大量的优化,比如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等。

1. synchronized 主要的使用方法

  • 修饰实例方法:给当前对象加锁,也就是 this
  • 修饰静态方法:给当前的类加锁,也就是给这个类的 class 对象加锁,基本上相当于这个 class 的全局锁了
  • 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码块之前要获得给定对象的锁

2. synchronized 的底层实现

  • synchronized 同步代码块实现使用的是 monitorentermonitorexit 指令,也就是分别指向同步代码块开始和结束的位置。
  • 但是在修饰方法的时候使用的是 ACC_SYNCHRONIZED 标识,JVM 通过此标识来判断此方法为同步方法。
  • 使用 monitor 的时候是通过监控对象的 对象头markword)中的相关属性来判断锁的状态。
    经典对象头表格
  • 再底层就是通过汇编指令了,intel 是 lock comxchg

3. 锁的升级过程

Lock Record
  • monitorenter 的时候,如果对象没有被锁定 01,虚拟机会首先在当前的栈中创建 Lock Record 的空间,用于存储锁对象的 Mark Word 的拷贝,这个过程叫做 Displaced Mark Word
  • Lock Record 是线程私有的数据结构,每一个线程都有一个可用的 Lock Record 列表,同时还有一个全局的可用列表。每一个被锁住的对象 Mark Word 都会与一个 Lock Record 关联。
Lock Record描述
Owner当线程成功拥有改锁后保存线程的唯一标识,其它情况下为 NULL
EntryQ关联一个系统互斥锁 semaphore,阻塞所有试图锁住 record 失败的线程
RcThis表示 blockedwaiting 在改 record 上的所有线程的个数
Nest用来实现重入锁的计数
HashCode从对象头拷贝过来的 HashCode,可能还包含分代年龄
Candidate用来避免不必要的阻塞或者等待线程唤醒,值为 010 表示没有需要唤醒的线程,1 表示需要唤醒一个继任线程来竞争锁
锁升级
  1. 进入 monitorenter 检查 Lock RecordDisplaced Mark Word
  2. 没有偏向锁的话,升级为偏向锁,偏向锁偏向第一个线程
  3. 偏向锁被争用的时候升级为轻量级锁,Displaced Mark Word,CAS 替换 Mark wordLock Record 的指针(线程争用)
    • 当线程竞争特别激烈的时候,关闭偏向锁效率反而更高
    • 默认是开启偏向锁的,-XX:-UseBiasedLocking
  4. 轻量级自旋超过 10,升级为重量级锁,生成或者服用 monitor 对象,mark wrod 指向 monitor,线程阻塞进入 _EntryList
    • 任何一个线程超过 10,或者 CPU 核心数的一半
    • 默认值为 10,可以通过 -XX:+PreBlockSpin=10 来指定次数

4. 其和 ReentrantLock 的区别

  • 都是可重入锁,一个线程获得了某个对象的锁,锁还没释放,是可以在此获取对象的锁。同一个线程获取锁,计数器 +1,计数器为 0 才能释放锁。
  • sychronized 依赖于 JVM,ReentrantLock 依赖于 Java 的 API,还需要使用 try finally 来进行 lock()unlock()
  • ReentrantLock 相对于 sychronized 增加了三个高级功能:等待可中断、可实现公平锁、可实现选择性通知性

高级功能

  • 等待可中断:中断等待锁的线程,lock.lockInterruptibly(),中断等待。
  • 公平锁:可实现公平锁(使用队列的 FIFO 实现)和非公平锁,可以通过 ReentrantLock(boolean fair) 构造方法来指定是否公平。公平锁就是先等待的线程先获得。
  • 选择性通知:sychronizedwait()notify() / notifyAll() 来实现等待通知机制。ReentrantLock 类也可以实现,需要借助 Condition 接口与 newCondition() 方法,它可以实现多路通知也就是一个 Lock 对象创建多个 Condition 实例,线程对象可以注册在 Condition 中,从而可以又选择性的进行线程通知,更灵活。Condition 实例的 signalAll() 只会唤醒在该 Condition 实例中的所有等待线程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值