zoukankan      html  css  js  c++  java
  • 并发编程之Synchronized原理

    一、基本使用

       1.Synchronized的作用。

    • 原子性:确保线程互斥的访问同步代码;
    • 可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
    • 有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”
       2.Synchronized的三种用法。Synchronized可以把任何一个非null对象作为"锁"。
    • 当synchronized作用在实例方法时,监视器锁(monitor)便是对象实例(this)
    • 当synchronized作用在静态方法时,监视器锁(monitor)便是对象的Class实例,因为Class数据存在于永久代,因此静态方法锁相当于该类的一个全局锁
    • 当synchronized作用在某一个对象实例时,监视器锁(monitor)便是括号括起来的对象实例。

      3.synchronized与Lock的区别

      1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

      2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

      3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

      4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

      5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

      6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

    类别synchronizedLock
    存在层次 Java的关键字,在jvm层面上 是一个类
    锁的释放 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 在finally中必须释放锁,不然容易造成线程死锁
    锁的获取 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
    锁状态 无法判断 可以判断
    锁类型 可重入 不可中断 非公平 可重入 可判断 可公平(两者皆可)
    性能 少量同步 大量同步
    二、同步原理

    1.数据同步需要依赖锁,那锁的同步又依赖谁?

     synchronized:在软件层面依赖JVM。

     j.u.c.Lock:在硬件层面依赖特殊的CPU指令。

    2.监视器锁(monitor):每个对象都是一个监视器锁(monitor)。

      Synchronized的语义底层是通过一个monitor的对象来完成。

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

    • 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;
    • 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;
    • 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权;

    4.monitorexit:执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

    三、同步概念

    1.Java对象头

      对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。

    • 实例数据:存放类的属性数据信息,包括父类的属性信息;
    • 对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐;
    • 对象头Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit),但是 如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。

      

     2.对象头中Mark Word与线程中Lock Record

     3.监视器(Monitor)

     任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态。

     Synchronized在JVM里的实现都是 基于进入和退出Monitor对象来实现方法同步和代码块同步。

     3.1 MonitorEnter指令:插入在同步代码块的开始位置,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的锁;

     3.2 MonitorExit指令:插入在方法结束处和异常处,JVM保证每个MonitorEnter必须有对应的MonitorExit;

     参考:深入分析Synchronized原理。

  • 相关阅读:
    无法嵌入互操作类型“ADOX.CatalogClass”。请改用适用的接口。
    编码:隐匿在计算机软硬件背后的语言(3)--二进制加法器
    编码:隐匿在计算机软硬件背后的语言(2)--二进制
    C#中Mutex的用法
    C#中创建二维数组,使用[][]和[,]的区别
    git同时存在两个账号(在同一台电脑上)——三步完成(已修正)
    C++之标准库vector
    C++之标准库map
    sublime和vscode 格式化Json ——两步走
    二十八、linux下权限管理chmod
  • 原文地址:https://www.cnblogs.com/wenxiangchen/p/11340799.html
Copyright © 2011-2022 走看看