关于synchronized 关键字
- 三种加锁方式分别是 方法、代码块和静态方法,加在方法上时相当于
synchronized(this)
,在静态方法上时相当于synchronized(xxx.class)
- JDK1.6以后对synchronized做了一些优化,有了锁膨胀的过程,实际上就是减少获取锁时用户态和内核态转换的过程
锁膨胀的三个阶段:
偏向锁:
轻量级锁优化同一个线程多次访问同步锁代码块的情况 1. 判断锁对象的对象头(MarkWord)是否处于可偏向状态 2. 如果是可偏向状态,通过CAS方式把自己的线程ID写入对象头,若写入失败表示存在锁竞争膨胀为轻量级锁,成功则获取到偏向锁。 3. 如果是已偏向状态,判断对象头中的线程ID和自己的是否一致,一致则无需获取锁直接执行,否则判断对应的线程是否已经结束,结束则重新偏向给当前线程,否则膨胀为轻量级锁。
重量级锁优化线程交替执行同步块的情况(此时同步代码块不存在竞争) 1. 判断同步对象是否处于无锁状态(锁标志位为 01,偏向标志位为 0) 2. 如果是无锁状态时尝试直接加锁,加锁前在当前线程的栈帧中建立锁记录(LockRecord)的空间,将锁对象的MarkWord拷贝到锁记录中(DispalyMarkWord),CAS尝试将锁对象的的MarkWord更新为指向LockRecord的指针,更新成功就获得轻量锁,否则进行自适应自旋知道获取锁或者失败膨胀为重量级锁。 3. 如果是有锁状态,判断锁对象的MarkWord是否指向当前线程的栈帧,是则已经获取到锁(锁重入),否则膨胀为重量级锁 4. 锁每次重入时会添加一条LocaRecord设置为null,锁释放时如果LockRecord中DisplayMarkWord是null则引用计数减一,否则尝试将DisplayMarkWord更新回锁对象,否则CAS尝试将DisplayMarkWord更新回锁对象中,失败则膨胀为重量级锁后再解锁
每个对象都会有一个monitor对象,重量级锁就是通过监视器monitor来实现的 重量级锁的几种状态: 1. Contention List 竞争队列:先进后出,候选队列为空时Owner线程会直接从Contention List取一个线程成为OnDeck线程去竞争锁 2. Entry List 候选队列:Contention List中那些有资格成为候选人的线程被移到Entry List,为了减少对Contention List的并发访问 3. Wait Set 等待队列:那些调用wait()方法被阻塞的线程 4. OnDeck:任何时刻最多Entry List中只能有一个线程被选中,去竞争锁,该线程称为OnDeck线程。 5. Owner:获得锁的线程称为Owner。 获取锁的过程: 1. 线程在进入竞争队列的前会首先尝试CAS获取锁(非公平锁) 2. Owner线程解锁时,如果候选队列为空会先在竞争队列中选取一个线程成进入候选队列 3. Owner线程解锁时,会从候选队列中先取一个线程成为OnDesk线程进行“竞争切换”(和正在CAS的线程),竞争成功则获取到锁。
网友评论