synchronized锁的5种状态

synchronized用的锁是存在java对象头中,上锁,改变的就是对象头。

synchronized锁有5种状态:无锁,偏向锁,轻量级锁,重量级锁,GC标志信息。

0.java应用启动的时候会有多个系统自带的线程执行synchronized代码块,必然会导致由偏向锁升级为轻量级锁,为了避免升级所带来额外的开销,
  此时jvm会关闭偏向锁,直接使用轻量级锁,默认等待4秒后再开启偏向锁,可用-XX:BiasedLockingStartupDelay参数设置时间。

1.无锁
在java应用启动后的4秒内new出来的对象,且该对象没被synchronized()锁住,此时是无锁状态。

此时对象头中的mark word(64位)存储数据如下:
25位无意义 | 31位存的是hashCode | 1位无意义 | 4位存的是对象的年龄(在年轻代每回收一次还存活则加1) | 1位存的是偏向锁状态(此时值是0) | 2位存的是锁状态(此时值是01)

2.偏向锁(用户态下即可执行)
在java应用启动后的4秒内new出来的对象,且该对象没被synchronized()锁住,此时因为开启了偏向锁,所以是偏向锁状态。
若该对象被synchronized()锁住,且大多数时间只有一个线程会使用这个锁,则会使用偏向锁,第一个获取锁的线程id会被存到mark word里面。

此时对象头中的mark word(64位)存储数据如下:
54位存获取了锁的线程id | 2位Epoch | 1位无意义 | 4位存的是对象的年龄(在年轻代每回收一次还存活则加1) | 1位存的是偏向锁状态(此时值是1) | 2位存的是锁状态(此时值是01)

3.轻量级锁,也叫自旋锁(用户态下即可执行)
当有两个线程竞争这把锁,首先撤销偏向锁升级为轻量级锁,然后两个线程的线程栈里面都创建一个lock record的记录,
最后两个线程竞争把lock record的指针存到mark word里面,谁先存成功谁获得锁,
另一个线程就不断地查看锁什么时候释放掉,再去加锁,所以叫自旋。
或者是在java应用启动后的4秒内,如果对对象加锁,由于关闭了偏向锁,所以直接使用了轻量级锁。

此时对象头中的mark word(64位)存储数据如下:
62位存的是线程栈里面lock record的指针 | 2位存的是锁状态(此时值是00)

4.重量级锁(需要切换到内核态执行,切换过程很耗时间资源)
当竞争加剧:有线程超过10次自旋或自旋线程数超过cpu核数一半(jdk1.6后jvm自己内部控制),升级为重量级锁,
切换成内核态,向系统申请调度资源,新建一个对象objectmonitor(由c++实现的),将竞争的线程都挂起并放到
objectmonitor中的一个队列中,然后由系统调度加锁解锁。

此时对象头中的mark word(64位)存储数据如下:
62位存的是objectmonitor的地址 | 2位存的是锁状态(此时值是10)

5.GC标志信息
当该对象没有其他引用的地方,被GC标志为可回收对象时,是GC标志状态

此时对象头中的mark word(64位)存储数据如下:
62位存的是GC需要的相关信息 | 2位存的是锁状态(此时值是11)

ps:
synchronized是可重入锁:
偏向锁和轻量级锁在重入的时候,对应线程栈的lock record个数加1,解开一个锁就pop一个lock record出去。
重量级锁是将重入的次数记录在objectmonitor的一个字段上。

用户态和内核态
进程在运行时一般都是用户态,此时权限是最低的。
但一旦要执行系统硬件层的操作,比如读写文件,网络传输等需要切换到内核态才能执行,但这个切换过程是很耗时的。

你可能感兴趣的:(并发)