JDK1.6之前的synchronized关键字一来就直接给对象加了一把重量级锁,频繁地在用户态和内核态之间切换,导致性能非常低。为了弥补synchronized的不足,大佬doug lee写了一个AQS框架,用Java语言实现了ReentrantLock。然后在JDK1.6之后,oracle优化了synchronized的锁过程,增加了锁的膨胀逻辑。当没有线程来调用synchronized修饰的代码时,synchronized为无锁态,当有一个线程调用时,synchronized由无锁态升级为偏向锁,当有多个线程都调用这块代码时,就会从偏向锁升级到轻量锁状态,这是没有获取到锁的线程就会进行自旋以获取锁。如果自旋太久一直没有获取到锁,就会升级为重量级锁。这个锁的膨胀过程大大提升了synchronized的性能。
对象锁
:
sy在JDK1.6之前:new Object();的时候jvm天然地维护一个管程monitor,monitor依赖底层的操作系统Mutex【互斥量】,mutex是由操作系统维护的【调操作系统的线程库Pthread】------------------------------为什么效率低呢?涉及到用户态与内核态之间的切换所以效率很低
基于sy锁太重影响性能的前提
开发出ReentrantLock锁,是基于java写的java类归于AQS框架。ReentrantLock可重入、公平性
oracle优化了synchronized的锁过程:
所以JDK1.6及之后,要是锁被一个线程占了,后面的线程就会自旋等待【不需要让出CPU的使用权,一直占着CPU】,浪费一点CPU资源,比阻塞自己回头等另外线程唤醒的效率高的多
只有重量级锁会依赖管程monitor
sy加锁
sy加在静态方法,锁是加在Class类上
sy加在普通方法,锁加在this,加在当前对象上
new 一个实例对象,对象锁
在同步代码块上sy(object)加锁:monitorenter和monitorexist
【有几个,避免发生异常,不释放锁】
在方法上加锁public static synchorized void decrStock(){}
在翻译字节码后会给方法的修饰符加上ACC_synchorized 一标志,这个标志会触发JVM 底层给代码块加monitorenter和monitorexist
对象怎么记录锁的状态的呢
对象的内存多大:8字节的整数倍
对象锁的状态就记录在Mark Word,Mark Word大小为4字节大小,32比特大小【32位虚拟机,64位的话也会进行指针压缩】
匿名偏向—>加入sy对象锁就会偏向锁,并且有指向哪个线程的记录---->多个线程抢3同一把锁的话就会转为轻量级锁---->当自旋等待的时间过长的话就会升级为重量级锁
在加锁的情况下,且处于偏向锁,这时候调用方法要回去hashcode,锁就会升级为轻量级锁,因为偏向锁没有记录hasdcode的地方,升级到轻量级锁后,mark word除了锁状态的其余部分会保存一个指针,指向一个地方,该地址保存初始状态的mark word 信息。如下图![
【稍微更改】注意:调用hashcode方法的时候,就进入无锁,再次进入会升级为轻量级锁
重量级锁hashcode存在管程里
\