多线程-synchronized
java对象头结构
Mark Word 数据结构
synchronized 锁升级流程
偏向锁-->轻量级锁-->重量级锁
样例代码:
public void test() {
synchronized (object) {
// 省略
}
}
偏向锁过程
当Thread-A 进入 synchronize 代码块时 ,先将 线程id 存入 object对象头中,(如果object调用过hashCode方法,那么Mark Word 中由于存放了hashCode 没有剩余的空间存放线程id会直接使用轻量级锁)表示该锁为Thread-A 专用,如果Thread-B也进入了 synchronize 代码块时,发现object对象头中已经存入了Thread-A的id,存在资源竞争关系则进行锁膨胀,使用轻量级锁
偏向锁结构图
当对象调用了hashCode方法后,Mark Word 中没有空间存储线程id ,偏向锁会失效
对象的偏向锁可以手动关闭
轻量级锁加锁过程
每一个线程运行有都有一个私有栈,当锁升级至轻量级锁时升创建一个锁记录,锁记录里有锁记录地址和指向object对象的指针。
升级至轻量级锁后,会尝试使用CAS操作将Mark Word 中的与锁记录地址进行交换来进行获取锁。假如Thread-A获取到锁,此时Thread-B进入同步代码块,会使用CAS操作进行获取锁 ,如果CAS操作失败后会根据操作系统来决定是否进行自旋,自旋过程中CAS操作成功了则结束。失败则进行锁膨胀,升级至重量级锁。
升级至重量级锁后会为object对象申请一个monitor对象(一个对象只能有一个monitor),将对象头中的锁记录更改为monitor地址,Monitor 中owner设置为当前锁的拥有者,当前线程进入EntryList 阻塞,等待拥有锁的线程释放锁后将entryList中阻塞的线程唤醒进行抢锁
(当线程抢到锁 发现不满足运行条件是 则调用wait方法时线程进入waitSet等待,等到条件满足时由owner线程使用notify或者notifyAll唤醒重新进入entryList 。wait和block 的线程都不占用cpu时间片,会释放锁,但是sleep不会释放锁资源)
monitor(重量级锁) 结构