Java中的锁机制

对象头

对象存放在内存中时,由三个部分组成:

  • 对象头
  • 实例数据:即该对象在堆中的数据
  • 对齐填充字节:无效字节,就是java用来让对象大小满足8字节的倍数,可加快寻址
    这里重点介绍对象头,对象头又分为mark word和类型数据指针,这里类型数据指针是一个指向该对象存储在方法区中的类信息。
    介绍就到这,mark word才是Java锁机制的重点

mark word

mark word数据结构
mark word中有锁标志位、分代年龄以及hashCode。
其中

synchronized锁

获取对象头中的mark word并将锁标志位置为1
在被synchronized修饰的代码块的头部和尾部加入monitorEnter和monitorExit。执行monitor指令的时候,线程尝试去获取mark word的锁标志位,如果锁标志位显示锁已经被别的线程占有,则当前线程阻塞。直至锁被释放,这时候当前线程被唤醒并将锁标志位置1,之后执行代码块中的代码。执行完后再去执行monitorExit指令,释放锁标志位。
如果synchronized去修改方法的话,则会对方法加上一个ACC_SYNCHRONIZED标识,代表该方法为一个同步方法,JVM会去判断方法是否为同步方法,若是的话则执行对应的同步调用。
本质上来说线程的唤醒和阻塞是通过notify和wait来实现的,是中断方法,会导致内核态和用户态的切换,属于重量级操作,切换时间长,某些情况甚至会超过线程执行任务的时间。
Java 6 对synchronized锁进行了优化,从执行时间的低到高分为了无锁、偏向锁、轻量级锁以及重量级锁。

无锁

没有对资源进行锁定,两种情况:

  1. 资源不会被竞争或者是线程安全的
  2. 通过其他机制来实现线程安全,如MySQL中的MVCC,JUI中的CAS

这里不过多对MVCC和CAS作太多介绍。 无锁时间最低,那为何java不执行用CAS呢? CAS存在以下问题:

  • ABA问题
  • 长时间自旋问题
  • 只能对一个变量保证原子性操作,多个变量力不从心
    所以锁的存在还是很有必要的

偏向锁

偏向锁也称偏爱锁,对象资源偏爱某个线程。在一个线程的情况下适用。
那么为什么只有一个线程的情况下要加锁呢?某些场景下,我们并不能确定只有一个线程会去访问资源。如果你确定的话,可以不加锁。
如何实现的呢?首先在锁标志位(即Mark Word的后三位)中的第一位置1,代表上锁。偏向锁就是101,这时候对象会把自己心爱的线程id记在自己的小本本上,这个线程来了就直接把资源给他。
那万一这个对象是个海王呢?喜欢多个线程或者说多个线程追求(请求)这个对象资源呢?那就把锁升级为轻量级锁(最后两位置00)

轻量级锁

这时候就需要线程去主动追求了,因为追求者太多了。
线程会在栈(线程私有)中开辟一个lock record,存放了Mark Word的副本以及owner指针。线程通过CAS获取锁,一旦获取锁之后,将owner指针指向该对象,表明这是自己的对象,别的线程要追求就先排队(CAS自旋等待,我来啊看看你们分手了没,什么?没分,行吧,那我过一会再来看看)。这是Mark Word中的前30位就是一个指向该线程lock record的指针。
到此,对象和线程之间就实现了一个双向绑定。此处的判断条件是线程数大于CPU核数的一半或者大于10
如果太多人判断他们分手,即多个线程进行自旋时,锁会升级为重量级锁。

重量级锁

monitor进行控制。

思考

Q:重量级锁和轻量级锁的区别?
A:线程之间是否存在资源竞争,不存在竞争即a线程1点的时候拿到资源,1.30用完,b线程2点的时候拿到资源…此时虽然可以用重量级,可以但是没有必要。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值