Java锁

1.锁
2.volitaile
3.final
4.轻量级锁,偏向锁,重量级锁

关于Synchronized的维基百科定义
https://wiki.openjdk.java.net/display/HotSpot/Synchronization
主要的图如下

image.png

synchronized(可重入)的作用范围
1.静态方法(锁住的是Class实例),字节码方法的访问标记包含,ACC_SYNCHRONIZED
2.实例方法(锁住的是当前类实例)
3.代码块(锁住的是当前类),编译后包含monitorenter和monitoreixt(可能会有多个,因为要确保锁的正常执行路径以及异常执行路径上都能够被解锁)

锁的代价由低到高
偏向锁,轻量级锁,重量级锁

偏向锁:针对的是锁仅会被同一线程持有的情况。

只会在第一次请求时采用CAS操作,在锁对象的标记字段中记录下当前线程的地址。
在之后的运行过程中,持有该偏向锁的线程加锁操作将直接返回。

轻量级锁:针对的是多个线程在不同时间段申请同一把锁的情况。

采用CAS操作,将锁对象的标记字段替换为一个指针,指向当前线程栈上的一块空间,存储着锁对象原本的标记字段。

重量级锁:针对的是多个线程同时竞争同一把锁的情况。

java虚拟机会采用自适应(根据以往自旋等待时是否能够获得锁,动态调整自旋时间,即循环数目,自旋也会影响公平的锁机制,处于阻塞状态的线程,并没有办法立刻竞争被释放的锁,但处于自旋状态的线程,很有可能立刻能获取到锁),自旋(当执行任务的时间小于线程调度的时间时是划算的),来避免在面对非常小的synchronized代码块时,仍然会被阻塞,唤醒的情况。

其次关于偏向锁和轻量级锁的区分

对象头的标记字段(mark word),最后两位是用来表示该对象的锁状态.
00代表轻量级锁
01代表无锁(或者偏向锁)
10代表重量级锁
11代表垃圾回收算法的标记

当进行加锁的时候,java虚拟机会首先判断是不是重量级锁。
不是,则
①在当前线程的栈中划出一块空间,作为该锁的记录,并且将锁对象的标记字段复制到锁记录中。
②尝试用CAS操作替换锁对象的标记字段

假设锁对象标记字段为X...XYZ
java虚拟机会首先比较该字段是否为X...X01
如果是,则替换为刚才分配的锁记录的地址。
如果不是,X...X01有两种可能
①该线程重复获取同一把锁,此时,java虚拟机会将锁记录清零,代表该锁被重复获取。
②其他线程持有该锁,java虚拟机会将这把锁膨胀为重量级锁,并且阻塞当前线程。

当进行解锁操作时
如果当前锁记录的值为0(会有多个锁记录,可以把所有锁记录看成一个栈结构,加锁即压入一条锁记录,解锁即弹出一条锁记录,当前锁即栈顶的锁记录),代表重复进入同一把锁,直接返回

否则,java虚拟机会尝试用CAS操作,比较锁对象的标记字段的值是否为当前锁记录的地址。
如果是,则替换锁记录中的值,也就是锁对象原本的标记字段。此时,成功释放这把锁
如果不是,则意味着已经膨胀为重量级锁。此时会进入重量级锁的释放过程,唤醒因为竞争该锁而被阻塞的线程。

32位系统中Mark Word的32bit空间中的25bit用于存储对象哈希码(HashCode),4bit用于存储对象的分代年龄,2bit用于存储锁标志位,1bit固定为0

HotSpot虚拟机对象头Mark Word

存储内容 标志位 状态
对象哈希码,对象分代年龄 01 未锁定
指向锁记录的指针 00 轻量级锁
指向重量级锁的指针 10 膨胀(重量级锁定)
空,不需要记录信息 11 GC标记
偏向线程ID,偏向时间戳,对象分代年龄 01 可偏向

你可能感兴趣的:(Java锁)