synchronized是什么
java关键字,是互斥锁,只允许一个线程进入被锁住的代码块
修饰实例方法时,锁是对象实例
修饰静态方法时,锁是类的class实例
修饰代码块时,锁是传入的对象
类对象实例和类class实例是两个锁,不冲突
synchronized原理
通过反编译(javap),当修饰方法时,编译器生成ACC_SYNCHRONIZED关键字来标识,当修饰代码块时,会在开头和结尾用monitorenter和monitorexit标识
以monitorenter为例,当线程执行到monitorenter时,就回去获取对象锁。对象锁由对象头的mark word和monitor对象组成,一个对象实例对应一个monitor对象,markword有指向monitor对象的指针,monitor对象存储着当前持有锁的线程以及等待锁的线程队列。如果锁计数器等于0,获取锁成功,计数器+1。如果失败,判断是否是当前线程持有锁,是,计数器+1,获取锁成功,否则获取锁失败
1.6之后做了什么优化
1.6前
synchronized是重量级锁,进入同步代码块时,monitor对象保存线程id,将monitor地址设置到对象的markword,将阻塞线程加入等待队列。它加锁依赖底层操作系统的Mutex相关指令实现,所以有用户态和内核态切换,性能损耗严重
1.6
引入偏向锁和轻量级锁,在Jvm层面实现加锁逻辑,不依赖底层操作系统,没有切换消耗
因此markword记录的锁状态有4种:无锁、偏向锁、轻量级锁、重量级锁
偏向锁:jvm认为没有竞争环境,直接在markword记录线程id,每次执行时比较线程id是否相等,相等就可以获取锁,执行同步代码;如果不相等,用CAS修改线程id,修改成功则获取锁;失败则说明有竞争,锁升级为轻量级锁。
轻量级锁:当前线程会在栈帧创建lockrecord,lockrecord拷贝markword信息,且有个owner指针指向锁对象。执行到同步代码时,用CAS尝试将markword指向线程的lockrecord,成功则获取到锁,失败则自旋,自旋一定次数后升级重量级锁。
重量级锁:线程执行到代码块,如果锁空闲则markword指向monitor记录线程id,monitor,如果锁被占了,再判断是否是当前占锁线程,是计数器+1,否则添加到等待队列;