JUC简笔1-synchronized的原理

谈到并发,我们首先想到的就是多线程,其次就是保证并发能正常进行的锁。在学习Java多线程时,最常见的一种锁就是通过synchronized实现,我们也把它叫作同步,包括了同步方法同步代码块

    public synchronized void test1() {
        //我是一个同步方法
    }

    public void test2() {
        synchronized(obj) {
            //我是同步代码块里的内容
        }
    }

先来看下利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式:
·1)对于普通同步方法,锁是当前实例对象。
·2)对于静态同步方法,锁是当前类的Class对象。
·3)对于同步方法块,锁是Synchonized括号里配置的对象。

好的,基础知识就说到这里。

老祖宗常说,饮水思源;我们也要继承老祖宗的优良传统,做到“用锁思源”。

那锁的源头是在哪呢? 换句话说,锁究竟是怎样跟对象扯上关系的呢?

The answer is synchronized用的锁是存在Java对象头里的!

什么是对象头?在这里简单讲一下
对象头分两部分(如果是数组则分三部分)如下图:
《JUC简笔1-synchronized的原理》

对于锁的存储,只用到了第一部分。第一部分Mark Word用于存储对象自身的运行时数据。

下图是无锁和有锁状态的32位虚拟机的Mark Word:
《JUC简笔1-synchronized的原理》
有锁状态:
《JUC简笔1-synchronized的原理》

看了上面两张图的“锁状态列”,想必大家也对对象头的锁有个大概了解了吧。

在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁、轻量级锁和重量级锁;这几个状态会随着竞争情况逐渐升级。

锁可以升级但不能降级!

1、偏向锁:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁。

获取锁的流程如下图:
《JUC简笔1-synchronized的原理》

撤销:
偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有正在执行的字节码)。
1)它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态;
2)如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程。

2、轻量级锁:引入轻量级锁的主要目的是在多没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁,则会尝试获取轻量级锁

1)轻量级锁加锁:
线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋(下文会有说明)来获取锁。

2)轻量级锁解锁:
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成
功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。
整个过程如下图所示:
《JUC简笔1-synchronized的原理》

3、重量级锁:就是我们理解的最常见的锁,并发时对线程进行阻塞,同步执行时间较长。

4、三种锁的对比
《JUC简笔1-synchronized的原理》

5、自旋锁
首先要说明的是,自旋锁并不是一种锁,而是一种锁的机制,就是让该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁。怎么等待呢?执行一段无意义的循环即可(自旋)。
它可以避免线程切换带来的开销,但是它占用了处理器的时间。如果持有锁的线程很快就释放了锁,那么自旋的效率就非常好,反之,自旋的线程就会白白消耗掉处理的资源,它不会做任何有意义的工作,典型的占着茅坑不拉屎,这样反而会带来性能上的浪费。所以说,自旋等待的时间(自旋的次数)必须要有一个限度,如果自旋超过了定义的时间仍然没有获取到锁,则应该被挂起。

参考:
《Java并发编程的艺术》——方腾飞
《深入分析synchronized的实现原理》——https://blog.csdn.net/chenssy/article/details/54883355
(图片来源:《Java并发编程的艺术》)

    原文作者:JUC
    原文地址: https://blog.csdn.net/weixin_38278878/article/details/80028729
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞