• 并发编程(二)原子性和Synchronized同步锁


    在《并发编程(一)》的文末提到了多线程的三大特性:原子性,可见性和有序性。本篇针对原子性展开。

    一、原子性

    1.什么是原子性

    一个操作(事务),不管分几步完成,最终结果只有两种可能,所有步骤都成功或者都失败。

    举例:i++操作有三步
    ①从内存读i的值
    ②i的值+1
    ③把结果写回内存(如果阻塞,则修改的值无法写回内存)

    在这里插入图片描述

    结论:非原子操作

    2.造成原子性的原因

    ①cpu高速缓存的不可见性
    在这里插入图片描述
    两个线程并行执行 i-- 操作,正确结果应该为8,运行结果为9。

    ②上下文切换造成的阻塞

    比如:
    硬件中断(鼠标和键盘操作)
    网络/O就绪
    线程阻塞,等待

    3.解决原子性问题之Synchronized同步锁

    Synchronized是一个关键字,用Synchronized修饰的内容(加了锁,并行变串行),在同一时刻只允许一个线程访问。具有排他性

    二、Synchronized

    1. Synchronized的使用

    在这里插入图片描述

    Synchronized位置

    ①② 修饰方法头,如果同时修饰静态方法(static),作用域是该类的所有实例对象。
    ③④⑤修饰方法体({}包裹的内容),()号里的内容就是作用域的范围。

    类名.class作用域也是该类的所有实例对象。
    其它使用方法的作用域为同一个对象实例。

    根据lock_flag来判断锁的状态,0表示无锁状态,1表示有锁状态。

    结论:①锁的标记或竞争依赖于对象 ② 锁的本质:竞争共享资源

    2. Synchronized的原理
    2.1、JVM的结构。

    JDK1.8取消了方法区和永久代,转而在内存中开辟了一个元数据区来存储类的信息,字段,方法,接口等,如下图的Meta区域。以前存在方法区的类信息,现在存到了本地内存的元数据区。
    jdk 1.8以后,

    2.2、类的加载,方法的执行

    在这里插入图片描述
    方法进栈,对象进堆。
    每个栈元素中存放了对应方法的局部变量,操作数栈,动态链接,返回地址等信息。
    如上图,mian()方法栈中存放了局部变量h-------->h指向堆中的Hello类实例对象h---------->实例h中的元数据指针指向元数据空间中的Hello类的信息。

    2.3、对象头MarkOop

    在这里插入图片描述
    在hotspot源码中,一个oop实例的构造如下(路径…\src\share\vm\oops\oop.hpp)

    class oopDesc {
       
      friend class VMStructs;
     private:
      volatile markOop  _mark; //对象头
      union _metadata {
       
        Klass*      _klass; //元数据指针
        narrowKlass _compressed_klass; //压缩指针
      } _metadata;
    ........
    ........
     // locking operations
      bool is_locked()   const;
      bool is_unlocked() const;
      ........
      ........
       // garbage collection
      bool is_gc_marked() const; //是否被gc标记
      ........
      // Age of object during scavenge
      uint age() const;  //gc年龄
      void incr_age();
       ........
       ........
      // identity hash; returns the identity hash key (computes it if necessary)
      // NOTE with the introduction of UseBiasedLocking that identity_hash() might reach a
      // safepoint if called on a biased object. Calling code must be aware of that.
      intptr_t identity_hash();//hashcode
      intptr_t slow_identity_hash();
      ........
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    hostspot教程

    markOop对象头: 对象的状态信息
    ①hashCode:对象在内存中的存放地址
    ②gc年龄:经历的gc次数(4bit最多15次)
    ③锁标记:是否持有锁
    ④monitor:锁监视器

    _klass 类元数据指针: 指向本地内存的元数据区,表示该对象是哪个类的实例;

    实例数据: 实际数据,变量等,如果变量是数组,则存储的是数组的指针;

    对齐填充: 64位的cpu读取内存数据,一次读8个byte(8个内存块)
    以前,分次读取,合并计算--------> 为了避免资源浪费,不足8个字节的部分会填充成8个字节,再读取;(空间换时间)

    2.4、monitor锁监视器

    路径:…\src\share\vm\runtime\objectMonitor.hpp

    class ObjectMonitor {
       
    ............
    //构造方法,实例化
    ObjectMonitor() {
       
        _header       = NULL; //对象头(markOop对象)
        _count        = 0; //抢占该锁的线程数量
        _waiters      = 0,//当前处于等待状态的线程数量
        _recursions   = 0; //锁重入次数
        _object       = NULL;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    聊聊 C++ 和 C# 中的 lambda 玩法
    历时数月钻研推流/对比各种流媒体服务程序/PK总结
    C语言--typedef的使用
    猫毛过敏却想养猫时?如何缓解猫毛过敏?宠物空气净化器推荐
    基于nodejs+vue美食外卖推荐网站系统源码
    激活MacBook的时候有个“文件保险箱磁盘加密“的选项,要不要开启
    Mocha MemoryBufferQueue 设计概述
    大数据数据挖掘系统可视化设计艺术
    Nginx实例配置详解
    matlab实现对极地投影数据的投影转换
  • 原文地址:https://blog.csdn.net/ZoeYen_/article/details/126029928