synchronized的实现原理

    在多线程并发编程中synchronized一直是元老级角色,很多人都会称呼它为重量级锁。

    利用synchronized实现同步的基础:Java中每一个对象都可以作为锁。集体表现为以下3中形式:

    1、对于普通同步方法,锁是当前实例对象

    2、对于静态同步方法,锁是当前类的Class对象

    3、对于同步代码块,锁是synchronized括号里配置的对象

    JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的。

public static void main(String[] args) {
    String s = "";
    synchronized (s){
        System.out.println(222);
    }
}
public synchronized void f(){
    System.out.println(111);
}

对上述代码进行编译后,使用javap -verbose 类名反编译这个类可以得到下列结果。



可以看到,同步代码块和同步方法使用不同的方式实现的。

    monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须要对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获取对象的锁。

synchronized被很多人成为重量锁。Java为了减少获取锁和释放锁带来的性能消耗而引入了偏向锁和轻量级锁,以及锁的存储结构和升级过程。

    1、对象头

    synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用三个字宽(Word)存储对象头,如果对象时非数组类型的,则用两字宽存储对象头。在32位虚拟机中,1字宽等于4字节   

Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。

在运行期间,Mark Word里存储的数据会随着锁标记位的变化而变化。Mark Word可能变化为存储一下4中数据。


    2、锁的升级

    锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。锁可以升级但不能降级。

    a、偏向锁:

            大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当锁对象第一次被线程获取的时候,虚拟机会将对象头中的标志位设为“01”,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中,如果CAS操作成功,持有偏向锁的线程以后在每次进入这个锁相关的同步块时,不用再进行任何同步操作,只是简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

            当有另一个线程去尝试获取这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏向后恢复到未锁定“01”或轻量级锁定“00”的状态,后续的同步操作就会按照轻量级锁那样执行。


    b、轻量级锁

        线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

        轻量级锁解锁时,会使用原子的CAS操作将锁记录中的对象头替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀到重量级锁。

    

    上图所示,两个线程同时竞争锁,导致锁膨胀。

     3、锁的优缺点对比


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`synchronized` 是 Java 中用于实现线程同步的关键字,它可以用来修饰代码块或方法。当某个线程执行一个被 `synchronized` 修饰的代码块或方法时,它会尝试获取对象的锁(monitor),如果锁没有被其他线程占用,则该线程会获取到锁,并继续执行代码块或方法;如果锁已经被其他线程占用,则该线程会被阻塞,直到获取到锁为止。 `synchronized` 的实现原理可以分为以下几个步骤: 1. 当一个线程尝试获取某个对象的锁时,它会先检查锁是否被其他线程占用。 2. 如果锁未被占用,则该线程会获取到锁,并继续执行代码块或方法。 3. 如果锁已经被其他线程占用,则该线程被阻塞,并被放入对象的等待队列中。 4. 当锁被释放时,等待队列中的线程会被唤醒,然后再次尝试获取锁。 5. 如果此时有多个线程都被唤醒,则它们会竞争获取锁,只有一个线程能够获取到锁,其他线程仍然被阻塞。 在 `synchronized` 中,锁是以对象为单位的。每个对象都有一个与之关联的锁,称为对象的监视器(monitor)。当一个线程进入某个对象的 `synchronized` 代码块或方法时,它会尝试获取该对象的监视器。如果监视器未被其他线程占用,则该线程获取到监视器并继续执行代码块或方法;如果监视器已经被其他线程占用,则该线程被阻塞,直到获取到监视器为止。当代码块或方法执行完毕后,该线程会释放监视器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值