Java对象头
以32位虚拟机为例
普通对象:
数组对象:
Mark Word结构:
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁(锁升级)
在JDK1.6之前synchronized属于重量级的锁,每次加锁都是通过操作系统来申请锁,所以会造成synchronized的效率比较低,尤其是随着时代的发展,多线程高并发越来越多,synchronized效率低的缺点就越来越明显,所以JDK1.6后对它进行了优化,不再是一开始就向操作系统申请锁,锁对象分成 无锁 - 偏向锁 - 轻量级锁 - 重量级锁四个状态。
在对象头的mark word中最低的三位代表锁状态,其中1位是偏向锁位两位是普通锁位,具体如下图:
这次主要关注mark word的后三位的变化,根据变化我们可以得出实际上对象的锁状态可以分成无锁、偏向锁、轻量级锁、重量级锁4个状态,GC过程中有对对象的锁降级,这个不是重点,先不管了。
锁升级流程开始:
1. 无锁 - > 偏向锁:
首先最开始对象是无锁状态,当一个线程准备对这个对象加锁前验证这三个字节发现了无锁状态,把对象是否偏向设置为1,锁标志位还是01,并把markword的线程ID改为当前线程ID,此时对象处于偏向锁状态。
2. 偏向锁 -> 轻量级锁:
一个线程继续对该该对象加锁,发现是偏向锁状态,判断偏向锁线程是否是当前线程,如果是则是直接进入,如果偏向线程不是当前线程,也就是存在锁竞争,那么就撤销偏向锁,升级为轻量级锁。
3. 轻量级锁实现:
轻量级锁实现方式是各个线程在自己的线程栈生成LockRecord ,用CAS操作将锁对象的markword设置为指向自己这个线程的LockRecord的指针,设置成功者得到锁,没有成功的将继续使用CAS一直循环直到成功,所以轻量级锁也叫自旋锁
4. 轻量级锁 -> 重量级锁:
当轻量级锁自旋次数达到最大次数,就会升级为重量级锁
这里CAS操作也比较重要,具体CAS原理大家可以转载到这片文章【什么是CAS机制?】 这位博主讲的还是挺详细的
Monitor(针对于重量级锁)
Monitor被翻译为监视器或管程
每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark Word中就被设置指向Monitor对象的指针。
多个线程争抢锁流程如下图:
Monitor结构图:
- 刚开始Monitor中Owner为nul
- 当Thread-2执行synchronize(obj)就会将Monitor的Owner(所有者)置为Thread-2,Monitor只能有一个Owner
- 在Thread-2上锁的过程中,如果Thread-3,Thread-4,Thread-5也来执行synchronize(obj),就会进入EntryList ,状态是BLOCKED
- Thread-2执行完同步代码块的内容,然后唤醒EntryList中等待的线程来竞争锁,竞争时是非公平的(并非是谁先来是获得锁,这里获得锁是随机的)
- 图中WaitSet中的Thread-0,Thread-1是之前获得过锁,但条件不满足(被调用了wait方法),然后进入WAITING状态的线程
注意:
- synchronize必须是进入同一个对象的monitor才有上述的效果
- 不加synchronize的对象不会关联监视器,不遵从以上规则