精华内容
下载资源
问答
  • java对象锁和类锁

    2020-07-23 14:20:04
    最近发现有很多初学java多线程基础的同学会问到对象锁和类锁会相互影响吗,本篇文章首先介绍下对象锁和类锁,然后从基础原理角度说明下具体关系。 对象锁(实例锁) 我们知道,类声明后,我们可以 new 出来很多...

    最近发现有很多初学java 多线程基础的同学会问到对象锁和类锁会相互影响吗,本篇文章首先介绍下对象锁和类锁,然后从基础原理角度说明下具体关系。

     

    对象锁(实例锁)

    我们知道,类声明后,可以 new 出来很多的实例对象,每个实例在 JVM 中都有自己的引用地址和堆内存空间,这些实例都是独立的个体。很显然,在实例上加的锁和其他的实例就没有关系,互不影响了。所以使用对象锁的情况,只有使用同一实例的线程才会受锁的影响,多个实例调用同一方法也不会受影响。

    通常我们使用实例锁的方式有下面三种:

    1、 锁住实体里的非静态变量

    2、 锁住 this 对象

    3、 直接锁非静态方法

     

    类锁

    对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。类锁是加载类上的,而类信息是存在 JVM 方法区的,并且整个 JVM 只有一份,方法区又是所有线程共享的,所以类锁是所有线程共享的。

    使用类锁的方式有如下方式:

    1、锁住类中的静态变量

    因为静态变量和类信息一样也是存在方法区的并且整个 JVM 只有一份,所以加在静态变量上可以达到类锁的目的。

    2、直接在静态方法上加 synchronized

    因为静态方法同样也是存在方法区的并且整个 JVM 只有一份,所以加在静态方法上可以达到类锁的目的。

    3、锁住 xxx.class

    对当前类的 .class 属性加锁,可以实现类锁。

     

    对象锁与类锁是否相互影响

    我们现在来梳理下,类锁和对象锁不是同一个东西,类锁是指类的Class对象的锁,对象锁是类的实例的锁,所以他们之间不会相互影响。例如线程A访问MyClass的类锁,线程B访问MyClass的实例mc的对象锁,他们之间不会受到相互影响

    展开全文
  • 本片博文 参考 “深入理解CAS算法原理” ,更多细节请查阅原...同一时刻,只有一个线程可以拥有 这个类和对象 如果一个线程想要获得某个或者对象的,需要询问虚拟机 当线程不再需要时,他再把还给虚拟机,...

    本片博文 参考 “深入理解CAS算法原理” ,更多细节请查阅原博客。 添加一些个人理解,如果理解有误,请提出宝贵意见

    1. Java中锁的基本介绍

    对象和类的锁
    • 只要是共享区域,就需要处理同步问题,下边是基本流程
      • 虚拟机给每个对象和类都分配一个锁
      • 同一时刻,只有一个线程可以拥有 这个类和对象
      • 如果一个线程想要获得某个类或者对象的锁,需要询问虚拟机
      • 当线程不再需要锁时,他再把锁还给虚拟机,这样虚拟机可以把锁再分配给其他申请锁的线程
    • 对象锁和类锁
      • 类锁其实通过对象锁实现的,当虚拟机加载一个类的时候,会会为这个类实例化一个 java.lang.Class 对象
      • 锁类时,实际上是锁住的Class对象
    1.2 监视器 (Monitors)
    • 监视器和锁同时被JVM使用,监视器主要功能是监控一段代码,确保在同一时间只有一个线程执行
    • 每个监视器都与一个对象相关联,
      • 当线程执行到监视器监视下的代码块中第一条指令,线程必须获取对被引用对象锁定
      • 获取锁之前,无法执行这段代码
    1.3多次加锁
    • 同一个线程可以对同一个对象进行多次加锁,
    • 每个对象维护着一个记录着被锁次数的计数器 : 同步代码块
      • 未被锁定的对象的该计数器为0
      • 当一个线程获得锁后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增
      • 当同一个线程释放锁的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁
    1.4 同步
    • Java语言提供了两种内置方式来使线程同步的访问数据:同步代码块和同步方法

    • synchronized 可以对代码块和对象加锁

      synchronized (this) 对代码块加锁,同一时间只有一个线程进入该段代码,其他线程类的线程不受影响

      synchronized(objA) 对objA对象加锁,同一时间只有一个获得该锁的线程进入代码,其他的线程均处于阻塞状态,比如妻子消费者和儿子消费者都对家庭加了锁,那么同一时间只有一个人可以进行消费

    2 . 乐观锁和CAS

    • Java中乐观锁和悲观锁

      JDK5之前,java用synchronized锁来控制同步问题 ; JDK5之后,增加了并发包java.util.concurrent.* , 其中的一些原子操作类(Atomic开头)采用的CAS算法就是一种乐观锁

    • 什么是CAS

      CAS就是 “Compare And Swap” , 先比较在交换 。 CAS是一种无锁算法 , 基本原理如下

      CAS有三个操作数,内存值V、旧的预期值、要修改的新值B。当且仅当 预期值A和内存值V相同时,将内存值V修改为B

    • CAS的理解

      CAS比较交换的伪码可以表示为下

      do{
      	备份旧数据; 
        基于旧数据构造新数据;
      }while(!CAS(内存地址,备份的旧数据,新数据))
      

      简单举例 :

      设存在两个线程 t1 、t2 , 同时去修改内存中的 一个变量56 ; 按照线程执行的规则,它们分别把56copy到自己的工作内存,修改,然后刷新回主存; 设t1 将值修改为57,并写回到主存中,对于t2来说,预期值是56但是内存中值成了57,内存值与预期值不一样,修改失败

      通俗解释:

      内存值和预期值进行比较时,如果相等就表示共享数据没有被修改,就可以替换新值继续向下运行,如果不相等,那说明共享数据已经被修改了,就放弃已经做得操作,然后重新执行刚才的操作

      CAS 操作是基于共享数据不会被修改的假设,当同步冲突出现机会很少时,这种假设会带来很大的性能提升

    • CAS开销

      CAS(比较并交换)是CPU指令级的操作,只有一步原子操作,所以非常快。CAS避免了请求操作系统来裁定锁的问题,不用麻烦操作系统,直接在CPU内部就搞定了

      Cache Miss:

      我们知道,CPU的硬件结构中 ,多喝处理器 存在多个CPU, 每个CPU又会存在缓存,管芯内还带有一个互联模块,使管芯内的两个核可以互相通信,数据以“缓存线”为单位在系统中传输。如下图所示

      [外链图片转存失败(img-CwqMvhJ3-1567494517501)(…/img/17.png)]

      CAS算法出现cache miss:

      设 CPU0 在对一个变量执行“比较并交换”(CAS)操作,而该变量所在的缓存线在 CPU7 的高速缓存中,就会发生以下经过简化的事件序列

      1. CPU0 检查本地高速缓存,没有找到缓存线

      2. 请求被转发到 CPU0 和 CPU1 的互联模块(interconnect),检查 CPU1 的本地高速缓存,没有找到缓存线。

      3. 请求被转发到系统互联模块(System Interconnect),检查其他三个管芯,得知缓存线被 CPU6和 CPU7 所在的管芯持有

      4. 请求被转发到 CPU6 和 CPU7 的互联模块,检查这两个 CPU 的高速缓存,在 CPU7 的高速缓存中找到缓存线

      5. CPU7 将缓存线发送给所属的互联模块,并且刷新自己高速缓存中的缓存线

      6. CPU6 和 CPU7 的互联模块将缓存线发送给系统互联模块

      7. 系统互联模块将缓存线发送给 CPU0 和 CPU1 的互联模块

      8. CPU0 和 CPU1 的互联模块将缓存线发送给 CPU0 的高速缓存

      9. CPU0 现在可以对高速缓存中的变量执行 CAS 操作

      CAS开销:

      最好的情况下(对某一个变量执行 CAS 操作的 CPU 正好是最后一个操作该变量的CPU,所以对应的缓存线已经在 CPU 的高速缓存中了), 消耗大约40纳秒 。最好情况下锁操作(一个“round trip 对”包括获取锁和随后的释放锁)消耗超过 60 纳秒,超过 100 个时钟周期。这里的“最好情况”意味着用于表示锁的数据结构已经在获取和释放锁的 CPU 所属的高速缓存中了

      锁操作比 CAS 操作更加耗时,因为锁操作的数据结构中需要两个原子操作

    • CAS算法应用:java原子类(automicXXX)

      Java1.7中 AtomicInteger.incrementAndGet()的实现源码为:

    在这里插入图片描述

    AtomicInteger.incrementAndGet的实现用了乐观锁技术,调用了类sun.misc.Unsafe库里面的 CAS算法,用CPU指令来实现无锁自增

    展开全文
  • synchronized实现同步的基础:Java中的每一个对象都可以作为。具体表现为以下3种形式。 对于普通同步方法,是当前实例对象(this)。 对于静态同步方法,是当前的Class对象。 对于同步方法块...

    synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式。

    • 对于普通同步方法,锁是当前实例对象(this)。

    • 对于静态同步方法,锁是当前类的Class对象

    • 对于同步方法块,锁是synchonized括号里配置的对象

       

    从JVM规范中可以看到synchonized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步;

    monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处;

    JVM要保证每个monitorenter必须有对应的monitorexit与之配对;

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

    线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁;

     

    同步方法,依靠的 是方法修饰符上的 ACC_SYNCHRONIZED 实现;

     

    synchronized用的锁是存在Java对象头里的,如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头,在32位虚拟机中,1字宽等于4字节,即32bit。

    Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”;

    在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态偏向锁状态轻量级锁状态重量级锁状态,这几个状态会随着竞争情况逐渐升级;

    锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁,这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率

     

     

    锁的表示

    Java里的锁,主要都是对对象进行加锁,如普通的synchronized非静态方法,就是对当前执行方法的对象进行加锁。

    那么怎么对对象进行加锁呢?对象的锁其实主要就是通过对象头的markOop(官方叫做MarkWord)进行表示的。markOop其实不是一个对象,只是一个字长的数据,在32为机器上,markOop为32个位,在64位上为64个位。

    markOop中不同的位区域存储着不同的信息,但是需要注意的一点是,markOop每个位区域表示的信息不是一定的,在不同状态下,markWord中存着不同的信息。接下来盗图一张:

     

    1 偏向锁

    很多情况下,一个锁对象不会发生被多个线程访问得情况,更多是被同一个线程进行访问,如果一个锁对象每次都被同一个线程访问,根本没有发生并发,但是每次都进行加锁,那岂不是非常耗费性能,所以偏向锁就被设计出来了;

    偏向,也可以理解为偏心;

    当锁对象第一次被某个线程访问时,它会在其对象头的markOop中记录该线程ID,那么下次该线程再次访问它时,就不需要进行加锁了;

    一个线程访问同步块并获取锁时,会在对象头栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁;

    如果测试成功,表示线程已经获得了锁;如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS对象头的偏向锁指向当前线程(线程ID);

    但是这中间只要发生了其他线程访问该锁对象的情况,证明这个锁对象会发生并发,就不能对这个对象再使用偏向锁了,会进行锁的升级

    2 轻量级锁

    有竞争且竞争不强烈时,JVM就会由偏向锁膨胀为轻量级锁,考虑到线程的阻塞和唤醒需要CPU从用户态转为核心态(增加CPU负担),而这种转换对CPU来说是一件负担很重的操作,因此没有获取到锁的线程不会进入阻塞状态,而是通过自旋的方式一直尝试获取锁,处于一种忙等状态,所以这种处理竞争的方式比较浪费CPU,但是响应速度很快

     

    3 重量级锁

    竞争激烈时,不会立刻进入阻塞状态而是会自旋一段时间看是否能获取锁如果不能则进入阻塞状态。

     

    三种锁的特点

    优点 缺点 使用场景
    偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级别的差距 如果线程间存在锁竞争,会带类额外的锁撤销的消耗 适用于只有一个线程访问同步块的场景
    轻量级锁 竞争的线程不会阻塞,提高了程序的响应速度 如果始终得不到锁竞争的线程,使用自旋会消耗CPU 追求响应时间,同步块执行速度非常快
    重量级锁 线程竞争几乎不使用自旋,不会消耗CPU 阻塞线程,响应时间缓慢 追求吞吐量,同步块执行速度较长

     

    来源:

    https://blog.51cto.com/wenshengzhu/2062647

    https://juejin.im/entry/5aad7773518825558252231a

    https://juejin.im/post/5ae6dc04f265da0ba351d3ff

    转载于:https://www.cnblogs.com/theRhyme/p/11084189.html

    展开全文
  • (1)基础Java中的每一个对象都可以作为。对于同步方法,是当前实例对象。对于静态同步方法,是当前对象的Class对象。...(2)同步的原理JVM规范规定JVM基于进入退出 Monitor 对象来实现方法同步...

    (1)基础

    Java中的每一个对象都可以作为锁。

    • 对于同步方法,锁是当前实例对象
    • 对于静态同步方法,锁是当前对象的Class对象
    • 对于同步方法块,锁是Synchonized括号里配置的对象

        当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。那么锁存在哪里呢?锁里面会存储什么信息呢?

    (2)同步的原理

    JVM规范规定JVM基于进入和退出 Monitor 对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明,但是方法的同步同样可以使用这两个指令来实现。

    monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁。

    2.1、Java对象头

    锁存在Java对象头里。如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,一字宽等于四字节,即32bit。

    | 长度 | 内容 | 说明 |

    | :------------- | :------------- |

    |32/64bit| Mark Word |存储对象的hashCode或锁信息等|

    |32/64bit| Class Metadata Address |存储到对象类型数据的指针|

    |32/64bit| Array length |数组的长度(如果当前对象是数组)|

    Java对象头里的Mark Word里默认存储对象的HashCode,分代年龄和锁标记位。32位JVM的Mark Word的默认存储结构如下:

    | | 25 bit |4bit|1bit是否是偏向锁|2bit锁标志位| | :------------- | :------------- | |无锁状态| 对象的hashCode| 对象分代年龄| 0| 01|

    在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据:


    2.2、锁的升级

    Java SE1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率

    展开全文
  • 笔者在【synchronized 用法与原理分析】一文中详细的讲解了synchronized的原理和用法,但是最近发现其实深究起来有些细微的方面还是不够牢固,比如synchronized锁住this,锁住普通对象类锁等等,在实际使用中的...
  • 文章目录1. 悲观(阻塞)1.1. 临界区与竞态条件1.1.1. 临界区1.1.2.... Java 对象头1.3.2. Monitor1.4. synchronized 原理1.4.1. 轻量级1.4.2. 重量级1.4.3. 偏向1.4.4. 自旋优化1.4.5. 同步消除1.5.
  • Java-- synchronized--学习原理总结monitor(重量级)轻量级加锁解锁膨胀 monitor(重量级) synchronized 关键字在使用的时候,往往需要指定一个对象与之关联,例如 synchronized(this),或者 ...
  • 概述 上篇博客我简单介绍了 Synchronized 的三种使用方法及部分特性,作为开发者,...在 java 代码中,对象创建完成后,在堆区分以下三个模块存储: 对象头 实例数据 填充数据 其中实例数据主要保存属性及数据信
  • 变量、常量、三种注释、生成doc文档、运算符、表达式 if语句、switch语句、嵌套判断 for语句、while语句、do-while语句、嵌套循环 类和对象 面向过程和面向对象的区别 的构成、访问修饰符、对象的内存分配 this...
  • 线程安全 定义 当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这...就像Java中的String。 绝对线程安全 不管...
  • Locksynchronized一、Java中的锁机制及Lock类锁的释放-获取建立的happens before 关系锁释放获取的内存语义锁内存语义的实现二、concurrent包的实现三、synchronized实现原理1、实现原理2、Java对象头3、Monitor...
  • java多线程(二)—— synchronized锁原理 1、java对象头 1.1、普通对象头   java中的对象是由三部分组成,分别是对象头、对象对齐填充;对象体是存储一个对象属性值父类的属性值的地方,并且jvm虚拟机中...
  • Java中的锁都是基于对象的锁,Java中的每一个对象都可以作为一个锁,我们常听到类锁其实也是对象锁,因为Java类只有一个class对象(一个Java类可以有多个实例对象,多个实例对象共享这一个Java类)。之所以有锁的...
  • Synchronized实现原理和锁优化

    千次阅读 2016-12-21 15:55:39
    Synchronized的三种...Java中每一个对象都可以作为,这是synchronized实现同步的基础。 synchronized 常见的三种用法如下: 普通同步方法,是当前实例对象 静态同步方法,是当前的class对象 同步方法...
  • 概述根据的添加到Java中的时间,Java中的,可以分为“同步“JUC包中的”。同步实现方式即通过synchronized关键字来进行同步,实现...原理同步原理是,对于每一个对象,有且仅有一个同步;不同的线
  • Java并发编程,自己在实际项目确实很少用到,经常学了就忘,忘了在学的恶心循环。通过再次的学习,掌握一些并发编程原理和...对于静态方法的同步,是当前的Class对象。 对于同步方法块,是Synchronized括号里...
  • java中的分类

    2017-08-15 20:27:00
    JVM规范规定JVM基于进入退出Monitor对象来实现方法同步代码块同步,但两者的实现细节不一样。代码块同步是使用monitorentermonitorexit指令实现,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有...
  • Lock接口实现是JDK5添加的内容,而synchronized在JDK6开始提供了一系列的优化,下面总结一下synchronized的实现原理和涉及的一些优化机制1.synchronized内部实现原理synchronized关键字在应用层的语义是可以...
  • Java锁详解

    千次阅读 多人点赞 2019-06-29 22:57:22
    文章目录什么是锁锁的实现方式锁涉及的几个重要概念类锁和对象锁(重要)synchronized实现原理 什么是锁 计算机还是单线程的时代,下面代码中的count,始终只会被一个线程累加,调用addOne()10次,count的值一定就...
  • Java并发编程,自己在实际项目确实很少用到,经常学了就忘,忘了在学的恶心循环。通过再次的学习,掌握一些并发编程原理和理清... 对于静态方法的同步,是当前的Class对象。 对于同步方法块,是Synchronize...
  • 一、Synchronized实现...Synchronized修饰静态同步方法:锁对象是当前的Class对象; Synchronized修饰同步代码块:锁对象是Synchronized后面括号里配置的对象; 2、Synchronized在JVM里的实现 那么Synchroniz...
  • Java中的机制及Lock 的释放-获取建立的happens before 关系 释放获取的内存语义 内存语义的实现 concurrent包的实现 synchronized实现原理 1、实现原理 2、Java对象头 3、Monitor 4、优化 5...
  • Synchronized锁原理

    2020-06-21 22:37:41
    Synchronized锁原理 前情提要 对于普通同步方法,锁是当前实例对象。 对于静态同步方法,锁是当前的Class对象。 对于同步方法块,锁是Synchonized括号里配置的对象。 Synchonized在JVM里的实现原理, JVM...
  • java中每个对象都有一个内置。当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码的当前实例(this)有关的。获得一个对象也称为获取,当程序运行到synchronized同步方法或代码块时...
  • 1、 Java 对象对象对象头包含两部分:运行时元数据(Mark Word)类型指针 (Klass Word) 运行时元数据 哈希值(HashCode),可以看作是堆中对象的地址 GC分代年龄(年龄计数器) 状态标志 线程持有的...
  • synchronized是java的一个关键字,它是jvm层上的,加锁方式有 方法锁,对象锁synchronized(this),类锁synchronized(Demo.Class) 它的用法有 普通同步方法,锁是当前实例对象 静态同步方法,锁是当前类的class对象 ...
  • Java中的synchronized实现原理1.6版本做了什么优化对象升级机制偏向轻量级锁Java语言实现的AQSLock接口ReentrantLock源码分析 synchronized synchronized用于并发场景,在1.6版本之前被称为重量级,也是...
  • java了解哪些

    2018-12-20 16:48:00
    使用的话,synchronize用于方法代码块,可以锁对象和类以及方法,Lock一般一块代码。并且Lock可以搭配condition使用。 实现原理的话,synchronized使用底层的mutex,需要系统调用,而Lock则使用AQS实现。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 433
精华内容 173
关键字:

java类锁和对象锁原理

java 订阅