Synchronized
对象锁,悲观锁
作用
- 可见性,清空工作区内存,同步主内存
- 有序性,原子性:同一时刻只允许一条线程访问资源,知道方法或代码块结束
使用和原理
- 对于非静态成员函数,锁加在对象上面的(方法上的访问标志)
- 对于静态成员函数,锁是加在class对象上(方法上的访问标志)
- 对于代码块加锁,加到特定对象(monitorenter,monitorexit)
- 综上,synchronized是在对象头的Mark Word区,存2个重要字段:锁标志位和占用该锁的thread ID
Q&A
优化
主要思想
- optimistic 乐观编程
- 表示对于共享资源的访问持乐观态度,认为在大多数情况下不会发生竞争,因此不需要加锁。先用最低成本去执行
- adaptive 自适应编程
- 表示根据运行时的情况动态调整程序的行为和策略,以适应不同的环境和需求。
- double check 双重校验
- 第一次校验:加锁之前,无锁状态下过滤掉大多数情况
- 第二次校验:加锁之后,防止第一次和加锁中间,共享数据被其他线程修改过
前置知识:对象头
锁状态 | 偏向模式 | 标志位 |
---|---|---|
未锁定 | 0 | 01 |
轻量级锁定 | 00 | |
重量级锁定 | 10 | |
GC标记 | 11 | |
可偏向 | 1 | 10 |
偏向锁
会有一个标志位,记录获取锁的线程,为了在无竞争状态下,让同一线程更快获取到锁
获取流程
- 首先获取对象的 MarkWord,判断是否处于可偏向状态
- 如果是可偏向状态,则通过 CAS 操作,把当前线程的 ID 写入到 MarkWord
- 如果 cas 成功, 表示已经获得了锁对象的偏向锁,接着执行同步代码块
- 如果 cas 失败,说明有其他线程已经获得了偏向锁,当前锁存在竞争,需要撤销已获得偏向锁的线程,并升级为轻量级锁
- 如果是已偏向状态,需要检查当前线程是不是记录线程( MarkWord 中存储的ThreadID 是否等于当前线程的 ThreadID非cas)
- 如果相等,不需要再次获得锁(加锁,解锁,更新MarkWord),可直 接执行同步代码块
- 如果不相等,偏向模式立刻结束,说明当前锁偏向于其他线程,需要撤销偏向锁并升级到轻量级锁
- 如果是可偏向状态,则通过 CAS 操作,把当前线程的 ID 写入到 MarkWord
轻量级锁
存在竞争的情况下,短期内不会阻塞吗,会先通过自旋尝试获取锁
- 先判断对象的锁状态是不是无锁状态(标志位01,偏向模式0)
- 然后在栈帧中复制一份对象头的MarkWord,cas尝试更新对象头MarkWord的指针
- 如果cas更新成功,标志位改成00
- 如果失败,double check一下MarkWord的指针,如果是当前线程的指针,更新成功
- 如果double check失败,意味着有多线程竞争,当前线程会自旋多尝试几次,当自旋超过一定次数,或者有第三个线程来竞争的时候,升级到重量级锁