深入分析synchronized的实现原理

Tao-Coder Tao-Coder     2022-10-16     474

关键词:

基础概念

  synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时可以保证共享变量对内存可见性。

 

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

    1. 普通同步方法,锁是当前实例对象
    2. 静态同步方法,锁是当前类的class对象
    3. 同步方法块,锁是括号里面的对象

  当一个线程访问同步代码块时,它首先是需要得到锁才能执行同步代码,当退出或者抛出异常时必须要释放锁。

底层实现

如何来实现这个机制呢?我们先看如下一段简单代码:

public class SynchronizedTest{
    public synchronized void test1(){

    }
    public void test2(){
        synchronized(this){
        }
    }

    public static void main(String []args){
        
    }
}

利用javap工具查看生成的class 文件信息来分析synchronize的实现

  从上图可以看出,同步代码块是使用monitorenter和monitorexit指令实现的,同步方法(在这看不出来需要看JVM底层实现)依靠的是方法修饰符上的ACC_SYNCHRONIZED实现。

  在java语言中存在两种内建的synchronized语法:1、synchronized语句;2、synchronized方法。对于synchronized语句当Java源代码被javac编译成bytecode的时候,会在同步块的入口位置和退出位置分别插入monitorenter和monitorexit字节码指令。而synchronized方法则会被翻译成普通的方法调用和返回指令如:invokevirtual、areturn指令,在VM字节码层面并没有任何特别的指令来实现被synchronized修饰的方法,而是在Class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的Class在JVM的内部对象表示Klass做为锁对象。

 

Java synchronized 包含两方面的含义

互斥

JVM 通过对象锁来实现互斥:

 

typedef struct object {  
    uintptr_t lock;  
    Class *class;  
} Object 

 

协作
协作是通过 Object wait, notify/notifyAll 方法实现的。

对应到JVM 的底层术语,这一机制叫做 monitor:

typedef struct monitor {
    pthread_mutex_t lock;
    Thread *owner;
    Object *obj;
    int count;
    int in_wait;
    uintptr_t entering;
    int wait_count;
    Thread *wait_set;
    struct monitor *next;
} Monitor;

在底层,进行获取 lock 动作到获得 lock 之间有一小段状态叫做 BLOCKED:  

 

void monitorLock(Monitor *mon, Thread *self) {
    if(mon->owner == self)
            mon->count++;
        else {
                if(pthread_mutex_trylock(&mon->lock)) {
                disableSuspend(self);

            self->blocked_mon = mon;
            self->blocked_count++;
            self->state = BLOCKED;

            pthread_mutex_lock(&mon->lock);
        
            self->state = RUNNING;
            self->blocked_mon = NULL;

                    enableSuspend(self);
                }
                mon->owner = self;
        }
}  

 

下面我们来了解两个重要的概念:Java对象头,Monitor。

 

对象头(Object Header):

 

  在JVM中创建对象时会在对象前面加上两个字大小的对象头,在32位机器上一个字为32bit,根据不同的状态位Mark World中存放不同的内容,如上图所示在轻量级锁中,Mark Word被分成两部分,刚开始时LockWord为被设置为HashCode、最低三位表示LockWord所处的状态,初始状态为001表示无锁状态。Klass ptr指向Class字节码在虚拟机内部的对象表示的地址。Fields表示连续的对象实例字段。

 

Monitor Record:

   Monitor Record是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表;那么这些monitor record有什么用呢?每一个被锁住的对象都会和一个monitor record关联(对象头中的LockWord指向monitor record的起始地址,由于这个地址是8byte对齐的所以LockWord的最低三位可以用来作为状态位),同时monitor record中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。如下图所示为Monitor Record的内部结构:

 

Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL;

EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。

RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。

Nest:用来实现重入锁的计数。

HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。

Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。

 

深入分析volatile的实现原理

引言在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改... 查看详情

java并发深入分析volatile的实现原理

通过前面一章我们了解了synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的volatile则是轻量级的synchronized。如果一个变量使用volatile,则它比使用synchronized的成本更加低,因为它不会引起线程上下文的切换和... 查看详情

聊聊并发深入分析volatile的实现原理

...ww.infoq.com/cn/articles/ftf-java-volatile引言在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时... 查看详情

深入理解多线程——synchronized的实现原理(代码片段)

synchronized,是Java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问到时,我们可以在代码中使用synchronized关键字对类或者对象加锁。那么,本文来介绍一... 查看详情

深入理解synchronize(代码片段)

本文参考引用,本人整理个人理解。地址点击1.实现原理synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。下面是一些同步的基础普通同步方法,... 查看详情

深入理解多线程——moniter的实现原理(代码片段)

...文,阅读该系列所有文章。在深入理解多线程(一)——Synchronized的实现原理中介绍过关于Synchronize的实现原理,无论是同步方法还是同步代码块,无论是AC 查看详情

java并发编程专题系列之深入分析synchronized(基础篇)

synchronized同步关键字简介synchronized是属于JVM层面的一个关键字,底层是通过一个monitor对象(管程对象)来完成,由于wait()/notify()等方法也依赖于monitor对象,所以只有在同步的块或者方法中才能调用wait/notify等方法synchronized同步代码... 查看详情

synchronized实现原理(代码片段)

记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized。对于当时的我们来说,synchronized是如此的神奇且强大。我们赋予它一个名字“同步”,也成为我们解决多线程情况的良药,百试不爽。但是,随着学习的深入,我... 查看详情

深入hotspot虚拟机源码探究synchronized底层实现原理万字总结synchronized(代码片段)

文章目录一、synchronized原理(1)首先准备好HotSpot源码(2)解压,使用vscode或者其他编辑器打开(3)初始monitor监视器锁(先了解后细说)(4)建立宏观概念(初始基本流程)... 查看详情

深入理解synchronized底层原理,一篇文章就够了!(代码片段)

...资源数据错乱,我们需要对线程进行同步,那么synchronized就是实现线程同步的关键字,可以说在并发控制中是必不可少的部分,今天就来看一下synchronized的使用和底层原理。synchronized的特性原子性所谓原子性就是... 查看详情

synchronized的实现原理及锁优化(代码片段)

... 记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized。对于当时的我们来说,synchronized是如此的神奇且强大。我们赋予它一个名字“同步”,也成为我们解决多线程情况的良药,百试不爽。但是,随着学习的深入,... 查看详情

011深入理解synchronized(代码片段)

一.概述在前面我们使用synchronized完成了一个原子方法的实现.现在我们需要深入的理解这个关键词. 二.使用方式  对于这个关键词我们的使用方式有四种:  [1]修饰普通方法  [2]修饰静态方法  [3]对实例对象加互斥锁... 查看详情

semaphore实现原理分析

synchronized的语义是互斥锁,就是在同一时刻,只有一个线程能获得执行代码的锁。但是现实生活中,有好多的场景,锁不止一把。比如说,又到了十一假期,买票是重点,必须圈起来。在购票大厅里,有5个售票窗口,也就是说... 查看详情

深入分析zookeeper的实现原理

zookeeper的由来  分布式系统的很多难题,都是由于缺少协调机制造成的。在分布式协调这块做得比较好的,有Google的Chubby以及Apache的Zookeeper。GoogleChubby是一个分布式锁服务,通过Google Chubby来解决分布式协作、Master选举等与... 查看详情

reentrantlock实现原理及源码分析

...k是Java并发包中提供的一个可重入的互斥锁。ReentrantLock和synchronized在基本用法,行为语义上都是类似的,同样都具有可重入性。只不过相比原生的Synchronized,ReentrantLock增加了一些高级的扩展功能,比如它可以实现公平锁,同时... 查看详情

深入分析object.finalize方法的实现原理

“物有本末,事有始终。知其先后,则近道矣”finalize如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发,下面通过一个例子说明finalize方法对垃圾回收有什么影响。publicclassFinalizeCase{privatestaticB... 查看详情

synchronize底层实现原理(代码片段)

????相信对Java程序员来说,synchronized关键字对大家来说并不陌生,当我们遇到并发情况时,优先会想到用synchronized关键字去解决,synchronized确实能够帮助我们去解决并发的问题,但是它会引起一些其他问题,比如最突出的一点就... 查看详情

深入分析java线程池的实现原理

前言线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:1、降低资源消耗;2、提高响应速度;3、提高线程的可管理性。Jav... 查看详情