视频合集

    Synchronized的底层实现原理,源码深度剖析!

    • 课程笔记
    • 问答交流

    在Java中,我们经常使用synchronized关键字来解决多线程的同步安全问题。更重要的是在Java面试中,synchronized属于必考点,需要掌握的知识点非常多。

    为了助大家掌握好synchronized,本节课我重点会讲解以下6点:

    1.Synchronized

    2.Synchronized方法锁、对象锁、类锁

    3.Synchronized的源码实现

    4.Synchronized的实现原理

    5.Monitor对象详解

    6.Synchronized的锁存储位置

    Synchronized

    Synchronized翻译为中文的意思是同步,也称之为”同步锁“,Synchronized关键字是java并发编程中必不可少的工具,它一次只允许一个线程进入特定代码段,从而避免多线程同时修改同一数据。

    Synchronized的作用

    在并发编程中存在线程安全问题,主要原因有:
    1.存在共享数据
    2.多线程共同操作共享数据

    关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。

    Synchronized的三种使用方式

    Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

    1.普通同步方法(实例方法):锁是当前实例对象 ,进入同步代码前要获得当前实例的锁。

    1. /**
    2. * 用在普通方法
    3. */
    4. private synchronized void synchronizedMethod() {
    5. System.out.println("--synchronizedMethod start--");
    6. try {
    7. Thread.sleep(2000);
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. System.out.println("--synchronizedMethod end--");
    12. }

    2.静态同步方法:锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁。

    1. /**
    2. * 用在静态方法
    3. */
    4. private synchronized static void synchronizedStaticMethod() {
    5. System.out.println("synchronizedStaticMethod start");
    6. try {
    7. Thread.sleep(2000);
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. System.out.println("synchronizedStaticMethod end");
    12. }

    3.同步方法块:锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

    1. /**
    2. * 用在类
    3. */
    4. private void synchronizedClass() {
    5. synchronized (SynchronizedTest.class) {
    6. System.out.println("synchronizedClass start");
    7. try {
    8. Thread.sleep(2000);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. System.out.println("synchronizedClass end");
    13. }
    14. }

    Synchronized的源码实现

    1. package com.mikechen.concurrent.lock;
    2.  
    3. public class SynchronizedAnalysis {
    4.  
    5. public synchronized void test1(){
    6.  
    7. System.out.println("welcome to mikechen");
    8. }
    9.  
    10. public void test2(){
    11. synchronized (this){
    12. System.out.println("welcome to mikechen");
    13. }
    14. }
    15.  
    16. }

    反编译后
    Synchronized的底层实现原理,源码深度剖析!-mikechen

    1、同步方法

    使用ACC_SYNCHRONIZED标记符隐式的实现

    2.同步代码块

    monitorenter(获取锁)、monitorexit(释放锁)
    两者虽然实现细节不同,但本质上都是对一个对象的监视器(monitor)的获取

    Synchronized的实现原理

    1.同步方法

    方法级的同步是隐式的,无须通过字节码指令来控制,JVM可以从方法常量池的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否声明为同步方法。

    当方法调用的时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否被设置

    如果设置了,执行线程就要求先持有monitor对象,然后才能执行方法,最后当方法执行完(无论是正常完成还是非正常完成)时释放monitor对象。

    在方法执行期间,执行线程持有了管程,其他线程都无法再次获取同一个管程。

    2.同步代码块

    同步代码块,synchronized关键字经过编译之后,会在同步代码块前后分别形成monitorenter和monitorexit字节码指令

    在执行monitorenter指令的时候,首先尝试获取对象的锁

    如果这个锁没有被锁定或者当前线程已经拥有了那个对象的锁,锁的计数器就加1

    在执行monitorexit指令时会将锁的计数器减1,当减为0的时候就释放锁。

    如果获取对象锁一直失败,那当前线程就要阻塞等待,直到对象锁被另一个线程释放为止。

    Monitor对象详解

    Synchronized的底层实现原理,源码深度剖析!-mikechen
    Synchronized的底层实现原理,源码深度剖析!-mikechen

    • Count用来记录该线程获取锁的次数
    • Entry List:那些有资格成为候选人的线程被移到Entry List
    • Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set
    • Owner:获得锁的线程称为Owner

    Synchronized的锁存储位置

    Synchronized的底层实现原理,源码深度剖析!-mikechen
    每个对象分为三块区域:对象头、实例数据和对齐填充

    标记字段:用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等,它是实现轻量级锁和偏向锁的关键

    类型指针:是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

    这节课就先讲到这里,后面一节课我会重点去讲解Synchronized的锁的详细升级过程(这也是大厂经常面试的考点)。

    评论交流
    1. JansenZhang

      synchronized是java提供的一个同步锁的关键字,java使用这个关键字来实现对代码块的加锁操作,从而保证在多线程的情况下只有一个线程能访问到被synchronized包住的代码块。
      synchronized根据使用位置的不同分为普通方法锁,静态方法锁和代码块锁,锁的对象分别是当前实例对象,当前类对象和自定义对象。
      虽然synchronized使用位置不同,但是底层的实现都是通过获取对象的监视器monitor来实现,在被synchronized标记的代码前后,会插入monitorenter和monitorexit指令。monitorenter会让当前线程获取用户对象的锁,如果获取成功,monitor的计数器就+1,这样别的线程就无法再获取到锁,当前线程执行sychronized的代码块,执行完成代码后会执行monitorexit,将monitor的计数器-1,当monitor的计数器=0的时候就代表解锁成功,允许别的线程获取锁。

    2. 路正银

      synchronized是java的关键字,当它用来修饰一个方法或者代码块的时候,能够保证在同一时刻只有一个线程执行该段代码。JDK1.5之后引入了自旋锁、锁粗化、轻量级锁、偏向锁来优化关键字的性能。
      synchronized的底层实现是通过一个monitor的对象来完成,具体的指令有monitorenter和monitorexit。

      • mikechen

        synchronized的锁优化特别重要的,严格来讲顺序是:无锁->偏向锁->轻量级锁->重量级锁,从轻量级锁到重量级锁会涉及到你上面提到的两个核心点:自旋锁与自适应自旋锁,刚好今天我讲了锁的升级,作业也提到了这点,回头可以作业输出后,你会有新的收获,路正银加油 ✗拳头✗

    3. 李鸿翼

      synchronized是同步锁,有三种使用方法,包括普通方法、静态方法、以及代码块锁对象。
      普通方法锁当前对象,静态方法锁当前类对象,代码块锁指定的对象。
      锁方法底层实现是用了一个acc同步标志位来标志该方法是否加锁,代码块锁是在代码块前面和后面加了moniterenter,moniterexit指令。
      虽然这两种方法实现有差异,但本质都是去获取一个moniter对象,获取到锁后,moniter对象的计数器加1,释放后,减一,如果变为0,代表锁已经完全释放,支持锁重入。

      每个对象包括对象头、实例数据、对齐填充。其中对象头里的标记字段包含了锁的相关信息

      • mikechen

        核心点都谈到了,synchronized的底层实现还有一个非常关键的点就是:锁的升级与优化,这个也是经常考察的点,刚好今天我讲到了这点,回头再把这个点补下就更好了 ✗咧嘴笑✗

    欢迎您,新朋友,感谢参与互动!