java 轻量级锁就是自旋锁吗_java中的偏向锁、轻量级锁、自旋锁、重量级锁

锁状态

锁信息存储在java对象的markword内容中

markword数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,它的最后2bit是锁状态标志位,用来标记当前对象的状态,对象的所处的状态,决定了markword存储的内容,如下表所示:

状态 标志位 存储内容

未锁定 01 对象哈希码、对象分代年龄

轻量级锁定 00 指向锁记录的指针

膨胀(重量级锁定) 10 执行重量级锁定的指针

GC标记 11 空(不需要记录信息)

可偏向 01 偏向线程ID、偏向时间戳、对象分代年龄

62e1b94bad5d

markdown-lock.png

偏向锁

偏向锁是jdk底层的优化,当只有一个线程会访问同步代码的时候,启用偏向锁

若遇到竞争(另一个线程去访问同步代码),会先通过CAS方式尝试获取偏向锁,若偏向锁未获取到存在竞争则升级成轻量级锁

升级轻量级锁前会释放偏向锁,释放偏向锁时会先进入安全点,发生stop the world的事情

轻量级锁

一开始线程进入同步代码时,拷贝对象的markdown信息,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”)

线程会在当前栈帧中建立锁记录Lock Record,并通过CAS更新对象对象头信息,将锁设置为00轻量锁

若设置轻量锁失败,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。

阻塞

线程阻塞状态需要操作系统的介入,在JVM和操作系统中切换线程需要耗费大量的系统资源(用户线程和内核的切换的消耗),因此在进入阻塞状态前线程会尝试通过自旋获取锁

自旋锁

如果持有锁对象的线程能在短时间内释放锁,则线程通过自选的方式避免用户线程与内核线程之间的切换。

自旋锁会消耗cpu资源,在自旋期间线程会一直占用CPU资源,所以需要设置自旋最大等待时间。

注意:自旋锁是非公平竞争

线程优化

1.根据线程竞争激烈程度,适当的开启或者关闭自旋锁、偏向锁、轻量级锁

2.减少锁的时间:不必要的方法不要放在同步代码中执行

3.减少锁的粒度:将一个锁拆成多个逻辑上的锁,增加并行度,从而降低锁的竞争

4.使用ConcurrentHashMap:通过多个Segment细分锁的粒度,put时先获取需要的Segment

5.LongAdder:LongAdder在AtomicLong的基础上将单点的更新压力分散到各个节点,在低并发的时候通过对base的直接更新可以很好的保障和AtomicLong的性能基本保持一致,而在高并发的时候通过分散提高了性能

6.LinkedBlockingQueue:对头和队尾分别加锁

拆锁的粒度不能无限拆,最多可以将一个锁拆为当前cup数量个锁即可;

7.使用读写锁:读写分离

8.使用cas(乐观锁)

9.消除缓存行的伪共享

Synchonized同步锁

普通同步方法--锁:当前实例对象

静态同步方法--锁:当前类的class对象

同步方法快--锁:Synchonized括号里的对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值