视频课程
小黑屋思过中,禁止观看!
评论并刷新后可见

您需要在视频最下面评论并刷新后,方可查看完整视频

视频课程
立即观看
付费视频

您支付费用,方可查看完整视频

¥{{user.role.value}}
课程视频
开始学习
会员专享

视频合集

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.普通同步方法(实例方法):锁是当前实例对象 ,进入同步代码前要获得当前实例的锁。

/**
* 用在普通方法
*/
private synchronized void synchronizedMethod() {
System.out.println("--synchronizedMethod start--");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("--synchronizedMethod end--");
}

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

/**
* 用在静态方法
*/
private synchronized static void synchronizedStaticMethod() {
System.out.println("synchronizedStaticMethod start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("synchronizedStaticMethod end");
}

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

/**
* 用在类
*/
private void synchronizedClass() {
synchronized (SynchronizedTest.class) {
System.out.println("synchronizedClass start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("synchronizedClass end");
}
}

Synchronized的源码实现

package com.mikechen.concurrent.lock;

public class SynchronizedAnalysis {

public synchronized void test1(){

System.out.println("welcome to mikechen");
}

public void test2(){
synchronized (this){
System.out.println("welcome to mikechen");
}
}

}

反编译后
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的底层实现还有一个非常关键的点就是:锁的升级与优化,这个也是经常考察的点,刚好今天我讲到了这点,回头再把这个点补下就更好了 ✗咧嘴笑✗