在并发中我们常常都会说到锁,那么锁到底是什么呢?
我对锁的理解如下:
- 每个Object都存在一把锁,这把锁存在对象头中,是一个抽象的概念
- 锁中记录了当前对象被那个线程所占用
我们的对象又主要由什么组成呢?
markWord示意图
Lock Record示意图
sychronized关键字可以保证我们的并发安全,那么它具体是怎么实现的呢?
- 当使用synchronized关键字修饰同步代码块的时候,我们锁的是配置对象
- 当使用synchronized关键字修饰同步方法时,我们锁的是实例对象
- 当使用synchronized关键字修饰静态同步方法时,我们锁的是类对象
- synchronized实现方法
-
主要表现在锁对象的对象头中
-
锁同步代码块中主要表现在 monitorentry和monitorexit语句中
如视频中的图示所示,我们可以将monitor看做一个房间,每个线程看做想要进入房间办公的人,但是一个房间一次只能有一个人在里面,假设1号嘉宾首先进入了monitor,那么这个时候其他人员都无法进入房间,但是如果1号嘉宾中途有事离开,那么此时我们的2号嘉宾可以乘机进入房间进行办公,一号嘉宾回来后也无法进入房间,进入等待队列,如果2号嘉宾办公完毕,就会唤醒等待队列中的嘉宾,此时只有1号嘉宾,于是1号嘉宾继续进入房间进行办公。 -
锁同步方法 ACC_SYNCHRONIZED
-
synchronized会造成线程的阻塞和唤醒,此时我们需要操作系统来协助我们进行操作,此时我们的操作系统需要从用户态切换为核心态
-
为避免频繁的切换我们的核心态和用户态,所以1.6的时候对synchronized进行了升级
- 偏向锁
- 不会主动释放锁
- 比较当前线程id和java对象头的threadID是否一致
- 一致则不释放锁
- 不一致则看之前的线程是否存活,不存活则重置为无锁状态,否则查找栈帧,判断是否继续持有,持有升级为轻量级锁,否则无锁
- 轻量级锁
- 升级:把锁对象的对象头MarkWord复制到线程1的栈帧占用,CAS替换对象头内容为锁记录地址
- 如果过程中线程2也获取锁,但是线程1已得手,则自旋锁线程2,即让线程2不断CAS
- 重量级锁
阻塞所有未持有锁的线程,即使用了monitor机制
- 偏向锁