【一知半解】synchronied

synchronized是什么

synchronized是java同步锁,同一时刻多个线程对同一资源进行修改时,能够保证同一时刻只有一个线程获取到资源并对其进行修改,因此保证了线程安全性。
synchronized可以修饰方法和代码块,底层实现的逻辑略有不同。

Object obj=new Object();
synchronized(obj){
    //do soming
}

编译后的代码为:

 ...

10 astore_2
11 monitorenter
12 aload_2
13 monitorexit
14 goto 22 (+8)
17 astore_3
18 aload_2
19 monitorexit
20 aload_3
21 athrow
22 return

当代码执行到 synchronize(obj)时,对应的字节码为 monitorenter进行加锁操作,代码执行完后就是 monitorexit进行锁的释放。两个 monitorexit是正常退出和异常退出两种情况下锁的释放。

public synchronized void test1(){
  //do somthing
}

当修饰方法时是在编译后的字节码上加上了 synchronized的访问标识

【一知半解】synchronied

Monitor机制

Monitor是一种同步机制,它的作用是保证同一时刻只有一个线程能访问到受保护的资源,JVM中的同步是基于进入和退出监视对象来实现的,是 synchronized的底层实现,每个对象实例都是一个Montor对象,Monitor对应的是底层的MonitorObject,是基于操作系统的互斥 mutex实现的。

【一知半解】synchronied
ObjectMonitor中有几个关键属性

属性 描述 _owner 指向持有ObjectMonitor对象的线程 _WaitSet 存放处于wait状态的线程队列 _EntryList 存放处于等待锁block状态的线程队列 _recursions 锁的重入次数 _count 用来记录该线程获取锁的次数

【一知半解】synchronied
  1. 进入monitor,被分配到 Entry List中,等待持有锁的线程释放锁,
  2. 当线程获取到锁后,是锁的持有者, owner指向当前线程
  3. 当线程进行 wait时进入 Wait Set,等待锁的持有者进行唤醒。

synchronized锁的实现原理

  1. 当代码执行到被 synchronized修饰的代码块或方法时,首先通过 monitor去获取对象实例的锁
  2. 当获取到锁时,会在对象实例的 对象头上添加锁标识位
  3. 没有获取到锁的线程,会进行到对对象实例的 entry list中进行等待
  4. 持有锁的线程的业务处理完后通过修改 对象头上锁标识位来进行释放锁
  5. 当线程进行 wait操作时,当前也会释放锁,然后进行 wait set区等待被唤醒
  6. entry list中处理等待的线程再次进行锁的竞争

Mark Word

一个对象的创建要经过这几步:

  1. 加载:如果对象的Class还没加载
  2. 链接:由符号引用转换为地址引用
  3. 初始化:执行Class的方法
  4. 开辟一个地址空间(可以使用TLAB技术进行优化,避免通过CAS产生的资源竞争)
  5. 初始化对象头信息
  6. 执行代码的方法
    7.返回对象地址
    一个对象有: 对象头实例数据对齐填充三部分组成
    【一知半解】synchronied
    对象头有: 对象标记(Mark Word)类型指针组成,如果对象是数组,对象头中还有 数组的长度
    在64位系统中,对象标记占8个字节,类型指针占8个字节,对象头共点16个字节
    对象标记中有 hashcode码GC年龄锁标记组成
    【一知半解】synchronied
    每个字节占8位,8个字节的MarkWord共占64位
    无锁的状态下,前25位没有使用,紧接着的32位保存了对象的 hashcode,在1位未使用,后面的4位对象的 GC年龄,后面的3位是锁标记位。

为什么GC年龄不能超过16

在MarkWord中可以看出GC年龄标记只有4位,二进制表示就是: 1111,对应的十进制就是15。

下面通过 jol进行查看MarkWord的信息,

<dependency>
  <groupid>org.openjdk.jol</groupid>
  <artifactid>jol-core</artifactid>
  <version>0.9</version>
</dependency>

无锁时

import org.openjdk.jol.info.ClassLayout;
public class MarkWordTest {
    public static void main(String[] args) {
        Hummy hummy=new Hummy();
        int hashCode = hummy.hashCode();
        System.out.println(hashCode);
        System.out.println("&#x4E8C;&#x8FDB;&#x5236;&#xFF1A;"+Integer.toBinaryString(hashCode));
        System.out.println("&#x5341;&#x516D;&#x8FDB;&#x5236;: "+Integer.toHexString(hashCode));
        System.out.println(ClassLayout.parseInstance(hummy).toPrintable());
    }
}
class Hummy{}

打印出的结果如下:

【一知半解】synchronied
可以看到对象的hashcode是: 6f496d9f,可以在左边的Value的找到hashcode值,只不过是反过来的。
最后1字节的 00000001包含了gc年龄和锁标记位。

加锁时

import org.openjdk.jol.info.ClassLayout;
public class MarkWordTest {

    public static void main(String[] args) {
        //java -XX:BiasedLockingStartupDelay=0
        Hummy hummy=new Hummy();
        synchronized (hummy){
            System.out.println(ClassLayout.parseInstance(hummy).toPrintable());
        }
    }
}
class Hummy{}

【一知半解】synchronied
最后一个 00000101的最后3位 101表示偏向锁

synchronized的优化

jdk1.6之前只有重量级锁,面在java1.6之后对synchronized的锁进行了优化,有偏向锁、轻量级锁、重量级锁,主要是因为重量级锁需要用到操作系统 mutex,操作系统实现线程之间的切换需要从用户态到内核态的,成本非常高。

锁 锁标识 场景 无锁 001 不受保护时 偏向锁 101 只有一个线竞争时 轻量级锁 00 竞争不激烈时 重量级锁 10 竞争非常激烈

锁升级的过程:

  1. 当访问同步代码时,首先判断markword是否是 &#x65E0;&#x9501;&#x72B6;&#x6001;(001)或者在偏向锁状态下markword中的线程id与当前线程id是否一样,如果是则把当前线程id通过CAS的方式设置到markword中
  2. 设置成功后则锁标记修改为(101),升级为偏向当前线程的 &#x7F16;&#x5411;&#x9501;(101),执行同步内的方法
  3. 如果失败,则由jvm进行偏向锁的撤消
  4. 当持有锁的线程运行到 &#x5B89;&#x5168;&#x70B9;时,检查偏向锁的状态
  5. 当持有锁的线程 &#x5DF2;&#x9000;&#x51FA;&#x540C;&#x6B65;&#x65B9;&#x6CD5;时,释放原线程持有的锁,变成无锁状态,到1处执行
  6. 当持有锁的线程 &#x8FD8;&#x5728;&#x540C;&#x6B65;&#x4EE3;&#x7801;中,则升级锁为 &#x8F7B;&#x91CF;&#x7EA7;&#x9501;(00),当前线程持有。
  7. 否则升级为 &#x91CD;&#x91CF;&#x7EA7;&#x9501;(10),先通过CAS的方法进行获取锁,获取锁失败后再自旋到一定次数(20)时,如果还是失败则进入堵塞状态,进入到 entry list区。

【一知半解】synchronied

Original: https://www.cnblogs.com/hitechr/p/16471016.html
Author: Hitechr
Title: 【一知半解】synchronied

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/590423/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 哈工大软件构造复习——关于“重载”与“重写”

    防扒链接: 写在前面 在复习软件构造课程的过程中,我对重载(Overload)与重写(Override)产生了不小的疑惑,因其名称的相似性,我时常会混淆二者的概念,因此特写下本篇博…

    Java 2023年6月9日
    089
  • MongoDB 分片规则

    生命本身毫无意义,只有死亡才能让你邃晓人性的真谛! Ideal is the beacon. Without ideal, there is no secure direction…

    Java 2023年6月9日
    095
  • PoweJob高级特性-MapReduce完整示例

    由于网上搜索 PowerJob MapReduce 都是设计原理,demo也展示个空壳子,没有演示Map到Reduce结果怎么传递,对于没有MR开发经验的人来说并没有什么帮助,所以…

    Java 2023年6月6日
    085
  • Java中的基本数据类型

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月5日
    085
  • JVM详解

    一、JVM的位置及体系结构 JVM作用在操作系统之上,而Java程序作用在jvm之上,其他的程序则与jvm并列 二、类加载器,及双亲委派机制 1.类加载器 作用:加载Class文件…

    Java 2023年6月13日
    068
  • 我是如何实现限流的?

    我是3y,一年 CRUD经验用十年的 markdown程序员👨🏻‍💻常年被誉为职业八股文选手 今天继续来更新austin项目的内容,主要讲讲 限流这块 01、为什么AUSTIN项目…

    Java 2023年6月9日
    077
  • Java-Collection、Map和Array之间的转换

    设个User类: public class User { private String userName; private String userId; private Strin…

    Java 2023年6月8日
    084
  • java: javamail 1.6.2 Create Receive Email using jdk 19

    尝试获取编码(pop3message.getEncoding()),则会获得此contentType(UTF-8一个) https://javaee.github.io/javam…

    Java 2023年5月29日
    092
  • IDEA对数据库、表、记录的(增删改查可视化操作)、数据库安全性问题的演示

    演示脏读 一个事物里面读到了另外一个事物没有提交的数据: read uncommitted 1.开启A,B窗口 2.分别查询A,B的隔离级别 select @@tx_isolati…

    Java 2023年6月15日
    070
  • Java 桥接方法

    桥接方法概念 Java中的桥接方法(Bridge Method)是一种为了实现某些Java语言特性而由编译器自动生成的方法。可以通过使用Java反射中 Method 类的 isBr…

    Java 2023年5月29日
    084
  • 数据视图

    视图作用 定义视图是设计数据库外模式的基本手段。视图能够为数据库系统提供一下优势: 1. &#x6570;&#x636E;&#x7684;&#x90…

    Java 2023年6月9日
    087
  • 设计模式 — Template Method(模板方法)

    直接上代码、先按原来开发步骤、在重构到模式、即在现成代码间寻找变化点、在使用对应的设计模式! 按流程执行代码 import org.junit.Test; // 程序库开发人员 c…

    Java 2023年6月16日
    073
  • springbean的生命周期

    作者:你的雷哥 出处:https://www.cnblogs.com/henuliulei/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保…

    Java 2023年5月30日
    088
  • JavaCV的摄像头实战之三:保存为mp4文件

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是《JavaCV的摄…

    Java 2023年6月8日
    094
  • iphone 开发学习笔记七

    本篇算是该系列最后一篇,虽不能说是很熟,但使用MonoTouch 开发的其本方法是掌握了,主要是完成当初的任务,实现地图开发包在iPhone上的实现。使用MonoTouch ,.N…

    Java 2023年5月29日
    053
  • 简单的python爬虫保存百度、360 搜索内容到数据库

    import requests import re from pyquery import PyQuery as Pq import pymysql.cursors connect…

    Java 2023年6月16日
    097
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球