Java ynchronized重量级锁的核心原理详解

目录
  • 1.monitor原理
  • 2.snychronized同步代码块原理
  • 3.synchronized同步方法原理
  • 4.重量级锁的开销
  • 总结

在JVM中,每个对象都关联一个监视器,这里的对象包含Object实例和Class实例。监视器是一个同步工具,相当于一个许可证,拿到许可证的线程即可进入临界区进行操作,没有拿到则需要阻塞等待。重量级锁通过监视器的方式保障了任何时间只允许一个线程通过受到监视器保护的临界区代码。

1. monitor原理

jvm中每个对象都会有一个监视器Monitor,监视器和对象一起创建、销毁。监视器相当于一个用来监视这些线程进入的特殊房间,其义务是保证(同一时间)只有一个线程可以访问被保护的临界区代码块。

每一个锁都对应一个monitor对象,在HotSpot虚拟机中它是由ObjectMonitor实现的(C++实现)

//部分属性
ObjectMonitor() {
    _count        = 0;     //锁计数器
    _owner        = NULL;
    _WaitSet      = NULL;  //处于wait状态的线程,会被加入到_WaitSet
    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
  }

本质上,监视器是一种同步工具,也可以说是一种同步机制,主要特点是:

  • 同步。监视器所保护的临界区代码是互斥地执行的。一个监视器是一个运行许可,任一线程进入临界区代码都需要获得这个许可,离开时把许可归还。
  • 协作。监视器提供Signal机制,允许正持有许可的线程暂时放弃许可进入阻塞等待状态,等待其他线程发送Signal去唤醒;其他拥有许可的线程可以发送Signal,唤醒正在阻塞等待的线程,让它可以重新获得许可并启动执行。

每个java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的mark word中就被设置指向 Monitor 对象的指针。

(1) 如果使用 synchronized 给obj对象上锁,obj对象的markword就会指向一个monitor锁对象;

(2) 刚开始 Monitor 中 Owner 为 null ;

(3) 当Thread-2线程持有monitor对象后,就会把monitor中的owner变量设置为当前线程Thread-2;

(4) 当Thread-3线程想要执行临界区的代码时,要判断monitor对象的属性Owner是否为null,如果为null,Thread-3线程就持有了对象锁,如果不为null,Thread-3线程就会放入monitor的EntryList阻塞队列中,处于阻塞状态Blocked。

(5) 在 Thread-2 上锁的过程中,如果Thread-4,Thread-5 也来执行 synchronized(obj),也会进入EntryList BLOCKED ;

(6) Thread-2 执行完同步代码块的内容,就会释放锁,将owner变量置为null,并唤醒EntryList 中阻塞的线程来竞争锁,竞争时是非公平的 ;

(7) 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲 wait-notify 时会分析

2. snychronized同步代码块原理

public class TestSynchronized {
    static final Object obj = new Object();
    static int i=0;
    public static void main(String[] args) {
        synchronized (obj){
            i++;
        }
    }
}

将上面的代码反编译为字节码文件:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: getstatic     #2         // 获取obj对象
         3: dup
         4: astore_1
         5: monitorenter		//将obj对象的markword置为monitor指针
         6: getstatic     #3
         9: iconst_1
        10: iadd
        11: putstatic     #3
        14: aload_1
        15: monitorexit			//同步代码块正常执行时,将obj对象的markword重置,唤醒EntryList
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit			//同步代码块出现异常时,将obj对象的markword重置,唤醒EntryList
        22: aload_2
        23: athrow
        24: return
      Exception table:
         from    to  target type
             6    16    19   any  //监测6-16行jvm指令,如果出现异常就会到第19行
            19    22    19   any

这两条指令的作用,我们直接参考JVM规范中描述:

monitorenter 指令:

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

(1) 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者

(2) 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

(3) 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit指令:

执行monitorexit的线程必须是持有obj锁对象的线程

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程释放monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出IllegalMonitorStateException的异常的原因。

3. synchronized同步方法原理

public class TestSynchronized {
    static int i=0;
    public synchronized  void add(){
        i++;
    }
}

对应的字节码指令:

 public synchronized void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field i:I
         3: iconst_1
         4: iadd
         5: putstatic     #2                  // Field i:I
         8: return

从反编译的结果来看,方法的同步并没有通过指令monitorentermonitorexit来完成不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

4. 重量级锁的开销

处于ContentionListEntryListWaitSet中的线程都处于阻塞状态,线程的阻塞或者唤醒都需要操作系统来帮忙,Linux内核下采用pthread_mutex_lock系统调用实现,进程需要从用户态切换到内核态。

用户态是应用程序运行的空间,为了能访问到内核管理的资源(例如CPU、内存、I/O),可以通过内核态所提供的访问接口实现,这些接口就叫系统调用。

pthread_mutex_lock系统调用是内核态为用户态进程提供的Linux内核态下互斥锁的访问机制,所以使用pthread_mutex_lock系统调用时,进程需要从用户态切换到内核态,而这种切换是需要消耗很多时间的,有可能比用户执行代码的时间还要长。

由于JVM轻量级锁使用CAS进行自旋抢锁,这些CAS操作都处于用户态下,进程不存在用户态和内核态之间的运行切换,因此JVM轻量级锁开销较小。而JVM重量级锁使用了Linux内核态下的互斥锁,这是重量级锁开销很大的原因。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • java中synchronized锁的升级过程

    目录 synchronized锁的升级(偏向锁.轻量级锁及重量级锁) java同步锁前置知识点 synchronized同步锁 java对象头 偏向锁 轻量级锁 重量级锁 关于自旋锁 打印偏向锁的参数 synchronized原理解析 一:synchronized原理解析 1:对象头 2:Synchronized在JVM中的实现原理 三.锁的优化 1.锁升级 2.锁粗化 3.锁消除 synchronized锁的升级(偏向锁.轻量级锁及重量级锁) java同步锁前置知识点 1.编码中如果使用锁可以

  • java synchronized 锁机制原理详解

    目录 前言: 1.synchronized 的作用: 2.synchronized 底层语义原理: 3. synchronized 的显式同步与隐式同步: 3.1.synchronized 代码块底层原理: 3.2.synchronized 方法底层原理: 4.JVM 对 synchronized 锁的优化: 4.1.锁升级:偏向锁->轻量级锁->自旋锁->重量级锁 4.1.1.synchronized 的 Mark word 标志位: 4.1.2.锁升级过程: 4.2.锁消除: 4.3

  • Java synchronized底层实现原理以及锁优化

    目录 一.概述 synchronized简介 synchronized作用 synchronized的使用 二.实现原理 三.理解Java对象头 四.JVM对synchronized的锁优化 1.偏向锁 2.轻量级锁 3.重量级锁 4.自旋锁 5.锁消除 6.锁粗化 总结 一.概述 synchronized简介 在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁.但是,随着 Java SE 1.6 对synchronized 进行了各种优化之后,有些情况下

  • java中synchronized Lock(本地同步)锁的8种情况

    目录 lock1 lock2 lock3 lock4 lock5 lock6 lock7 lock8 Lock(本地同步)锁的8种情况总结与说明: * 题目: * 1.标准访问,请问是先打印邮件还是短信 Email * 2.email方法新增暂停4秒钟,请问是先打印邮件还是短信 Email * 3.新增普通的hello方法,请问先打印邮件还是hello hello * 4.两部手机,请问先打印邮件还是短信 SMS * 5.两个静态同步方法,1部手机,请问先打印邮件还是短信 Email * 6.两

  • Java的锁机制:synchronized和CAS详解

    目录 一 为什么要用锁 二 synchronized怎么实现的 三 CAS来者何人 四synchronized和CAS孰优孰劣 轻量级锁 重量级锁 总结 提到Java的知识点一定会有多线程,JDK版本不断的更迭很多新的概念和方法也都响应提出,但是多线程和线程安全一直是一个重要的关注点.比如说我们一入门就学习的synchronized怎么个实现和原理,还有总是被提到的CAS是啥,他和synchronized关系是啥?这里大概会让你对这些东西有一个认识. 一 为什么要用锁 我们使用多线程肯定是为了提

  • Java实现synchronized锁同步机制

    目录 synchronized 实现原理 适应性自旋(Adaptive Spinning) 锁升级 Java 对象头 偏向锁(Biased Locking) 偏向锁获取 偏向锁释放 关闭偏向锁 轻量级锁(Lightweight Locking) 轻量级锁获取 轻量级锁解锁 重量级锁 锁消除(Lock Elimination) 锁粗化(Lock Coarsening) 文末总结 synchronized 是 java 内置的同步锁实现,一个关键字实现对共享资源的锁定.synchronized 有

  • Java ynchronized重量级锁的核心原理详解

    目录 1.monitor原理 2.snychronized同步代码块原理 3.synchronized同步方法原理 4.重量级锁的开销 总结 在JVM中,每个对象都关联一个监视器,这里的对象包含Object实例和Class实例.监视器是一个同步工具,相当于一个许可证,拿到许可证的线程即可进入临界区进行操作,没有拿到则需要阻塞等待.重量级锁通过监视器的方式保障了任何时间只允许一个线程通过受到监视器保护的临界区代码. 1. monitor原理 jvm中每个对象都会有一个监视器Monitor,监视器和

  • Java synchronized偏向锁的核心原理详解

    目录 1.偏向锁的核心原理 2.偏向锁的撤销 3.偏向锁的膨胀 4.偏向锁的好处 总结 1. 偏向锁的核心原理 轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作. Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现 这个线程 ID 是自己的就表示没有竞争,不用重新 CAS.以后只要不发生竞争,这个对象就归该线程所有. public class Main { static final Objec

  • Java synchronized轻量级锁的核心原理详解

    目录 1.轻量级锁的原理 2.轻量级锁的分类 1.普通自旋锁 2.自适应自旋锁 3.轻量级锁的膨胀 总结 问题: 什么是自旋锁? 说一下 synchronized 底层实现原理? 多线程中 synchronized 锁升级的原理是什么? 1. 轻量级锁的原理 引入轻量级锁的主要目的是在多线程竞争不激烈的情况下,通过CAS机制竞争锁减少重量级锁产生的性能损耗.重量级锁使用了操作系统底层的互斥锁(Mutex Lock),会导致线程在用户态和核心态之间频繁切换,从而带来较大的性能损耗. 轻量级锁的使用

  • java并发等待条件的实现原理详解

    前言 前面介绍了排它锁,共享锁的实现机制,本篇继续学习AQS中的另外一个内容-Condition.想必学过java的都知道Object.wait和Object.notify,同时也应该知晓这两个方法的使用离不开synchronized关键字.synchronized是jvm级别提供的同步原语,它的实现机制隐藏在jvm实现中.作为Lock系列功能中的Condition,就是用来实现类似 Object.wait和Object.notify 对应功能的. 使用场景 为了更好的理解Lock和Condit

  • Java进阶之高并发核心Selector详解

    一.Selector设计 笔者下载得是openjdk8的源码, 画出类图 比较清晰得看到,openjdk中Selector的实现是SelectorImpl,然后SelectorImpl又将职责委托给了具体的平台,比如图中框出的 linux2.6以后才有的EpollSelectorImpl Windows平台是WindowsSelectorImpl MacOSX平台是KQueueSelectorImpl 从名字也可以猜到,openjdk肯定在底层还是用epoll,kqueue,iocp这些技术来实

  • Java 自定义Spring框架与核心功能详解

    目录 Spring核心功能结构 核心容器 spring-beans和spring-core模块 spring-context模块 spring-context-support模块 spring-context-indexer模块 spring-expression模块 AOP和设备支持 数据访问与集成 Web组件 通信报文 集成测试 bean概述 在上一讲中,我们对Spring的基本使用进行了一个简单的回顾,接下来,我们就来看一下Spring核心功能结构. Spring核心功能结构 Spring

  • Java基础学习之反射机制原理详解

    目录 一.什么是反射 二.反射的原理 三.反射的优缺点 四.反射的用途 五.反射机制常用的类 六.反射的基本使用 一.什么是反射 (1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法.本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息. (2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM.通过反射,可

  • Java原生操作JDBC连接以及原理详解

    一.简介 JDBC全称又叫做Java DataBase Connectivity,也就是Java数据库连接,说白了就是用Java语言来操作数据 库,提供统一API访问数据库操作. 二.原理 JDBC主要是用于java连接数据库的,能连接什么数据库不固定,其实能连接很多种数据库,而且一般来说可以连接oracle和mysql,通常也是这两种.但是既然JDBC能连接这么多的数据库,开发起来太麻烦了,于是sun公司那些人想出了一个办法,我定义一套规范,大家都按照这个规范来,实现自己公司访问数据库的实现.

  • redis分布式锁的实现原理详解

    首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 1.互斥性.在任意时刻,只有一个客户端能持有锁. 2.不会发生死锁.即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁. 3.具有容错性.只要大部分的Redis节点正常运行,客户端就可以加锁和解锁. 4.解铃还须系铃人.加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了. 下边是代码实现,首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码: <depe

  • Java实现JDK动态代理的原理详解

    目录 概念 案例 静态代理 JDK动态代理模式 原理分析 真相大白 概念 代理:为控制A对象,而创建出新B对象,由B对象代替执行A对象所有操作,称之为代理.一个代理体系建立涉及到3个参与角色:真实对象(A),代理对象(B),客户端. 其中的代理对象(B)起到中介作用,连通真实对象(A)与客户端,如果进一步拓展,代理对象可以实现更加复杂逻辑,比如对真实对象进行访问控制. 案例 需求:员工业务层接口调用save需要admin权限,调用list不需要权限,没权限调用时抛出异常提示. 静态代理 /**

随机推荐