班花-synchronized

①synchronized原理代码编译之后,会在代码块前后加上monitorenter和monitorexit字节码指令,以来操作系统底层互斥来实现。他的作用主要就是实现原子性操作和解决共享变量的内存可见性问题。

①synchronized原理

代码编译之后,会在代码块前后加上monitorenter和monitorexit字节码指令,以来操作系统底层互斥来实现。他的作用主要就是实现原子性操作和解决共享变量的内存可见性问题。

当有多个线程进入到代码块monitor的时候,首先会进入一个entryList 集合,当有一个线程获取到monitor的时候,就赋值给当前线程,计数器+1,其他线程进入到waitSet集合。

当获取到锁的线程执行完毕,调用wait方法,把当前线程置为null,计数器-1 变为0。调用notify或者notifyAll ,唤醒其他线程到entryList 去竞争锁。类似循环。

班花-synchronized

需要注意的是如果是在方法上使用synchronized:

一个特殊标志位:ACC_SYNCHRONIZED

同步方法的时候,一旦执行到这个方法,就会先判断是否有标志位,然后,ACC_SYNCHRONIZED会去隐式调用刚才的两个指令:monitorenter和monitorexit。

所以归根究底,还是monitor对象的争夺。一般提到ACC_SYNCHRONIZED标志位就好。

② synchronized锁优化

在我们常用的Hotspot虚拟机中,对象在内存中布局实际包含3个部分:

对象头

实例数据

对齐填充

而对象头包含两部分内容,Mark Word中的内容会随着锁标志位而发生变化,所以只说存储结构就好了。

对象自身运行时所需的数据,也被称为Mark Word,也就是用于轻量级锁和偏向锁的关键点。具体的内容包含对象的hashcode、分代年龄、轻量级锁指针、重量级锁指针、GC标记、偏向锁线程ID、偏向锁时间戳。

存储类型指针,也就是指向类的元数据的指针,通过这个指针才能确定对象是属于哪个类的实例。

如果是数组的话,则还包含了数组的长度 。

班花-synchronized

Jdk在1.6之后做了很多优化,性能上。有些情况下也不再是重量级锁了。

优化机制包括自适应锁、自旋锁、锁消除、锁粗化、轻量级锁和偏向锁。

升级方向:

班花-synchronized

锁的状态从低到高依次为无锁->偏向锁->轻量级锁->重量级锁,升级的过程就是从低到高,一般来说不可逆(也有人说降级在一定条件也是有可能发生的,感兴趣自查吧)。

自旋锁:由于大部分时候,锁被占用的时间很短,共享变量的锁定时间也很短,所以没有必要挂起线程,用户态和内核态的来回上下文切换严重影响性能。自旋的概念就是让线程执行一个忙循环,可以理解为就是啥也不干,防止从用户态转入内核态,自旋锁可以通过设置-XX:+UseSpining来开启,自旋的默认次数是10次,可以使用-XX:PreBlockSpin设置。

自适应锁:自适应锁就是自适应的自旋锁,自旋的时间不是固定时间,而是由前一次在同一个锁上的自旋时间和锁的持有者状态来决定。

偏向锁:当线程访问同步块获取锁时,会在对象头和栈帧中的锁记录里存储偏向锁的线程ID,之后这个线程再次进入同步块时都不需要CAS来加锁和解锁了,偏向锁会永远偏向第一个获得锁的线程,如果后续没有其他线程获得过这个锁,持有锁的线程就永远不需要进行同步,反之,当有其他线程竞争偏向锁时,持有偏向锁的线程就会释放偏向锁。可以用过设置-XX:+UseBiasedLocking开启偏向锁。

轻量级锁:JVM的对象的对象头中包含有一些锁的标志位,代码进入同步块的时候,JVM将会使用CAS方式来尝试获取锁,如果更新成功则会把对象头中的状态位标记为轻量级锁,如果更新失败,当前线程就尝试自旋来获得锁。

简单点说,偏向锁就是通过对象头的偏向线程ID来对比,甚至都不需要CAS了,而轻量级锁主要就是通过CAS修改对象头锁记录和自旋来实现,重量级锁则是除了拥有锁的线程其他全部阻塞。

班花-synchronized

注意:

一个线程获取锁之后,另外一个线程处于阻塞或者等待状态,前一个不释放,后一个也一直会阻塞或者等待,不可以被中断。

值得一提的是,Lock的tryLock方法是可以被中断的。

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/102967.html

(0)

相关推荐