锁——待优化

申明:听马士兵老师的公开课,自己总结的课程小结,遗失了很多内容。

 

用户态和内核态。重度锁是在内核态创建的,需要惊动操作系统(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,此时对象不为空,但是成员变量还是默认值,然后另一个线程也调用单例,将拿到半初始化的对象(对象不为空,但成员变量还没有初始化)。

posted @ 2020-05-17 23:11  hellodingc  阅读(194)  评论(0)    收藏  举报