synchronized锁的优化

           

目录

      1.底层实现

       2.锁升级(优化部分)

       3.轻量级锁的加锁解锁

1.加锁       

2.解锁


         synchronzied同步锁保证线程安全依赖于操作系统互斥锁(Mutex Lock),消耗资源的同时还需一直等待,没有额外的尝试获取机制,不过在java1.6以后进行了优化。

      1.底层实现

        使用synchronized保证线程安全,其实就是通过加synchronzied锁来保证一个线程在整个执行过程中不会被其他线程干扰(原子性)。

        而利用javap(可以查看java源文件)反编译后的代码中,我们可以看到,其底层实现利用了monitorenter/monitorexit来实现同步的:

11: astore_1
12: monitorenter   //监视器进入
13: aload_0
14: dup
15: getfield  	#2              	// Field sharedState:I
18: dup_x1
…
56: monitorexit    //监视器退出

         因为synchronzied是通过对象内部的监视器(monitor)来实现同步的。

        那么关于对象内部的监视器(monitor)就值得我们做更深层次的探究了。

        在java虚拟机实现规范有关monitor的描述是:每个对象有一个监视器(monitor),线程通过执行monitorenter指令尝试获取monitor的所有权,获取到以后monitor会处于锁定状态(表示被占用)。

          获取monitor的所有权过程如下:

         1.如果monitor的进入数为0,则该允许线程进入,然后monitor的进入数设置为1,该线程即为monitor的所有者,代表持有锁(即图中:当前对象的监视器的的Owner指向当前线程Thread-2,其他线程不是阻塞就是等待);

         2.如果线程已经占有该monitor,只是重新进入,则monitor的进入数+1;

         3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

        2.锁升级(优化部分)

          在JVM底层实现锁的过程中,有三种类型的锁:偏斜锁  轻量级锁  重量级锁

          优化之前(Java6 之前),synchronized的实现完全依靠操作系统内部的互斥锁,来进行用户态到内核态的切换(同步操作相当于就是采用重量级锁),极其消耗系统资源。

          Java 6之后,在Oracle JDK中,JVM对synchronized进行了改进,提供了三种不同的Monitor即偏斜锁、轻量级锁、重量级锁,以此来应对不同线程环境下的锁的切换,实现了锁的升级、降级。

偏向锁轻量级锁重量级锁
核心单线程执行(未出现线程并发)多个线程串行不支持并发依赖OS互斥锁
实现机制该线程在后续访问时便会自动获得锁,尽量减少不必要的轻量级锁的执行路径多线程串行的访问同一个加锁对象利用互斥锁实现线程之间切换,需从用户态转换到核心态,切换成本极其高。
性能降低获取锁带来的消耗,提高性能每次执行,都消耗了重复的加锁与解锁的性能开销。性能最差,效率低
切换加锁的代码有多个线程调用时,升级为轻量级锁同一时间不同线程访问同一把锁,升级为重量级锁

        3.轻量级锁的加锁解锁

           轻量级锁依赖CAS操作Mark Word来试图获取锁,如果成功,就是用普通的轻量级锁,否则,进一步升级为重量级锁。

          Mark Word的结构如下:

Mark Word
bitfileds标志位(2bit位)状态特征

指向当前锁记录的指针

ptr to  lock record

00轻量级锁自旋
    hash                age  0/1(是否偏向锁)   01无锁
Thread ID epochage01偏向锁只需比较Thread ID

指向重量级锁monitor的指针

ptr to heavyweight monitor

10重量级锁依赖mutex(OS的互斥)
11可GC用于标记GC

                注:lock标志位:2位二进制,锁状态标记位。

                       thread: 持有偏向锁的线程ID。

                       ptr_to_lock_record:指向栈中锁记录的指针。

          1.加锁

         (1) 进入同步代码块是,如果对象锁状态无锁状态(lock 标志位“01”,biased_lock标志位 “0”),则虚拟机首先在当前线程的栈帧创建一个Lock Record锁记录空间,用于存储锁对象目前Mark Word的拷贝

        (2)拷贝当前对象头中的Mark Word复制Lock Record中。

        (3)拷贝成功后,虚拟机尝试将

        对象的Mark Word中的 当前锁记录的指针 更新为指向 Lock Record的指针,并将

        线程的Lock Record中的 owner指针 指向 该对象的Mark Word 。若更新成功,继续步骤(4),否则步骤(5)。

        (4)若当前线程对象的指向都更新成功,那么代表该线程拥有了该对象的锁,并且对象的Mark Word的lock标志位设置为“00”,即表示此对象处于轻量级锁定状态。

        (5)若更新失败,虚拟机首先会检查对象的Mark Word是否已经指向当前线程的栈帧

        若已指向,说明当前线程已经拥有了该对象的锁,即可直接进入同步块继续执行;

        未指向,则说明多个线程同时竞争该对象的锁,轻量级锁就要升级为重量级锁,lock标志位变为“10”,Mark Word中存储的就是是指向重量级锁的指针,后面等待锁的线程进入阻塞状态,当前线程尝试使用自旋获取锁。(自旋:为了不让线程阻塞,而采用循环去获取锁的过程)。

        2.解锁

        (1)通过CAS指令,尝试把线程中复制的Mark Word(该对象的Mark Word替换为当前的Mark Work。

        (2)如果替换成功,整个同步过程完成

        (3)如果替换失败,说明有其他线程尝试过获取该锁,就要升级重量级锁,即在释放锁的同时,通知其他线程重新参与锁的竞争。

        

        

        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值