锁——待优化
申明:听马士兵老师的公开课,自己总结的课程小结,遗失了很多内容。
用户态和内核态。重度锁是在内核态创建的,需要惊动操作系统(0x80中断)。
无锁策略使用比较交换(cas,compare and swap)来鉴别线程冲突。cas思路如下:读取内存中的数值,计算修改后,将修改前的值与内存值比较,如果相同,则说明没有其他线程修改过,就将修改后的值写入内存;如果不同,则说明被改过,重复上述过程,知道写入成功。
两个疑问:1、ABA问题,2、比较时被修改了咋办。1的解决办法,没次修改都加时间戳作为版本号,2的问题,cas是CPU指令级的操作,调用的汇编命令lock cmpxchg完成,这是原子操作,不可中断。CAS避免了请求操作系统来裁定锁的问题,不用麻烦操作系统,直接在CPU内部就搞定了。
重量级锁(创建锁需要惊动操作系统————0x80中断),synchronize
锁的升级
先了解一下对象的内存占用空间,jol,java object layout。jol包含四部分内容,依次是:mark word;class pointer 标记属于哪个类;前2者称为对象头。instance data 成员变量;padding 补齐数位,意义不大。
mark word的3个作用:锁态、GC的分代、hashcode。锁态用低3位表示,其中轻量级锁、重量级锁只用到低2位,偏向锁和无锁用第3位加以区分。
可以使用org.openjdk.jol把对象的内存空间打印出来,这个包可以在https://www.mvnjar.com/下载,使用如:ClassLayout.parseInstance(o).toPrintable()。
锁态标记位和锁的升级如下:

无锁态-偏向锁-自旋锁-重量级锁
偏向锁:只有一个线程使用,没有其他线程竞争,使用线程贴个标签标识该资源正在被使用
自旋锁:轻度竞争,一个线程在使用,其他少量线程等待资源,但拿不到资源,所以不停询问可以用了吗,ha所以叫自旋锁。(自旋锁是一种乐观锁。);等待自旋锁的线程不会释放CPU资源,占用cpu做无用功。
重量级锁:最常见的是synchronize,重度竞争,等待的线程多,占用时间长,这种情况下,等待线程释放cpu资源,进入等待池,效率更高。
自旋锁的优势是轻度竞争条件下,避免操作系统层面申请锁,来提升效率,达到这个目的的方法就是等待线程不释放,一直询问。如果出现重度竞争的情况,还不如释放资源,申请os级的锁更快。
偏向锁一定更快吗?
不一定。。。。
volatile 线程读取某个变量时,不会在线程缓存创建这个变量的备份,而是直接从内存中读取。volatile适合一个线程写,多个线程读的场景。
volatile还能禁止指令重排序。
对象的创建过程:
汇编码:
0 new #2 <T> 向内存申请空间,成员变量为默认值
1 dup
2 invokespecial #3 <T.<init>> T的构造方法,初始化成员变量
3 astore1 与类建立关联
4 return
单例为什么可不可以用volatile。
class T{
private static volatile T(){}
}
半初始化状态,如果线程1调用单例,创建对象,执行了0,然后指令重排序,执行了3,没执行2,此时对象不为空,但是成员变量还是默认值,然后另一个线程也调用单例,将拿到半初始化的对象(对象不为空,但成员变量还没有初始化)。
浙公网安备 33010602011771号