java中synchronized锁的升级过程

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

synchronized锁的升级(偏向锁、轻量级锁及重量级锁)

java同步锁前置知识点

  • 1.编码中如果使用锁可以使用synchronized关键字,对方法、代码块进行同步加锁
  • 2.Synchronized同步锁是jvm内置的隐式锁(相对Lock,隐式加锁与释放)
  • 3.Synchronized同步锁的实现依赖于操作系统,获取锁与释放锁进行系统调用,会引起用户态与内核态切换
  • 4.jdk1.5之前加锁只能使用synchronized,1.6引入Lock同步锁(请求锁基于java实现,显式加锁与释放、性能更优)
  • 5.jdk1.6对于Synchronzied同步锁提出了偏向锁、轻量级锁、重量级锁的概念(其实是对synchronized的性能优化,尽可能减少锁竞争带来的上下文切换)
  • 6.无论是使用synchronized还是Lock,线程上下文切换都是无法避免的
  • 7.Lock相对synchronized的性能优化的其中一点是:在线程阻塞的时候,Lock获取锁不会导致用户态与内核态的切换,而synchronized会(看第3点)。但是线程阻塞都会导致上下文切换(看第6点)
  • 8.java线程的阻塞与唤醒依赖操作系统调用,导致用户态与内核态切换
  • 9.前面说的用户态与内核态切换发生的是进程上下文切换而非线程上下文切换

本文主要关注synchronized锁的升级。

synchronized同步锁

java对象头

每个java对象都有一个对象头,对象头由类型指针和标记字段组成。

在64位虚拟机中,未开启压缩指针,标记字段占64位,类型指针占64位,共计16个字节。

锁类型信息为标记字段的最后2位:00表示轻量级锁,01表示无锁或偏向锁,10表示重量级锁;如果倒数第3位为1表示这个类的偏向锁启用,为0表示类的偏向锁被禁用。

如下图,图片来源wiki

左侧一列表示偏向锁启用(方框1),右侧一列表示偏向锁禁用(方框3)。1和3都表示无锁的初始状态,如果启用偏向锁,锁升级的步骤应该是1->2->4->5,如果禁用偏向锁,锁升级步骤是3->4->5。

我用的jdk8,打印了参数看了下,默认是启用偏向锁,如要是禁用: -XX:-UseBiasedLocking

关于偏向锁还有另外几个参数:

注意BiasedLockingStartupDelay参数,默认值4000ms,表示虚拟机启动的延迟4s才会使用偏向锁(先使用轻量级锁)。

偏向锁

偏向锁处理的场景是大部分时间只有同一条线程在请求锁,没有多线程竞争锁的情况。看对象头图的红框2,有个thread ID字段:当第一次线程加锁的时候,jvm通过cas将当前线程地址设置到thread ID标记位,最后3位是101。下次同一线程再获取锁的时候只用检查最后3位是否为101,是否为当前线程,epoch是否和锁对象的类的epoch相等(wiki上说没有再次cas设置是为了针对现在多处理器上的cas操作的优化)。

偏向锁优化带来的性能提升指的是避免了获取锁进行系统调用导致的用户态和内核态的切换,因为都是同一条线程获取锁,没有必要每次获取锁的时候都要进行系统调用。

如果当前线程获取锁的时候(无锁状态下)线程ID与当前线程不匹配,会将偏向锁撤销,重新偏向当前线程,如果次数达到BiasedLockingBulkRebiasThreshold的值,默认20次,当前类的偏向锁失效,影响就是epoch的值变动,加锁类的epoch值加1,后续锁对象会重新copy类的epoch值到图中的epoch标记位。如果总撤销次数达到BiasedLockingBulkRevokeThreshold的值(默认40次),就禁用当前类的偏向锁了,就是对象头右侧列了,加锁直接从轻量锁开始了(锁升级了)。

偏向锁的撤销是个很麻烦的过程,需要所有线程达到安全点(发生STW),遍历所有线程的线程栈检查是否持有锁对象,避免丢锁,还有就是对epoch的处理。

如果存在多线程竞争,那偏向锁就要升级了,升级到轻量级锁。

轻量级锁

轻量级锁处理的场景是在同的时间段有不同的线程请求锁(线程交替执行)。即使同一时间段,存在多条线程竞争锁,获取到锁的线程持有锁的时间也特别短,很快就释放锁了。

线程加锁的时候,判断不是重量级锁,就会在当前线程栈内开辟一个空间,作为锁记录,将锁对象头的标记字段复制过来(复制过来是做一个记录,因为后面要把锁对象头的标记字段的值替换为刚才复制这个标记字段的空间地址,就像对象头那个图片中的pointer to lock record部分,至于最后2位,因为是内存对齐的缘故,所以是00)。然后基于CAS操作将复制这个标记字段的地址设置为锁对象头的标记位的值,如果成功就是获取到锁了。如果加锁的时候判断不是重量级锁,最后两位也不是01(从偏向锁或无锁状态过来的),那就说明已经有线程持有了,如果是当前线程在(需要重入),那就设置一个0,这里是个栈结构,直接压入一个0即可。最后释放锁的时候,出栈,最后一个元素记录的就是锁对象原来的标记字段的值,再通过CAS设置到锁对象头即可。

注意在获取锁的时候,cas失败,当前线程会自旋一会,达到一定次数,升级到重量级锁,当前线程也会阻塞。

重量级锁

重量级就是我们平常说的加的同步锁,也就是java基础的锁实现,获取锁与释放锁的时候都要进行系统调用,从而导致上下文切换。

关于自旋锁

关于自旋锁,我查阅相关资料,主要有两种说明:

1、是轻量级锁竞争失败,不会立即膨胀为重量级而是先自旋一定次数尝试获取锁;

2、是重量级锁竞争失败也不会立即阻塞,也是自旋一定次数(这里涉及到一个自调整算法)。

关于这个说明,还是要看jvm的源码实现才能确定哪个是真实的:

打印偏向锁的参数

如下:

-XX:+UnlockDiagnosticVMOptions

-XX:+PrintBiasedLockingStatistics

我在main方法循环获取同一把锁,打印结果如下:

    public static void main(String[] args) {
        int num = 0;
        for (int i = 0; i < 1_000_000000; i++) {
            synchronized (lock) {
                num++;
            }
        }
    }

synchronized原理解析

一:synchronized原理解析

1:对象头

首先,我们要知道对象在内存中的布局:

已知对象是存放在堆内存中的,对象大致可以分为三个部分,分别是对象头、实例变量和填充字节。

  • 对象头zhuyao是由MarkWord和Klass Point(类型指针)组成,其中Klass Point是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据。如果对象是数组对象,那么对象头占用3个字宽(Word),如果对象是非数组对象,那么对象头占用2个字宽。(1word = 2 Byte = 16 bit)。
  • 实例变量存储的是对象的属性信息,包括父类的属性信息,按照4字节对齐。
  • 填充字符,因为虚拟机要求对象字节必须是8字节的整数倍,填充字符就是用于凑齐这个整数倍的。

通过第一部分可以知道,Synchronized不论是修饰方法还是代码块,都是通过持有修饰对象的锁来实现同步,那么Synchronized锁对象是存在哪里的呢?答案是存在锁对象的对象头的MarkWord中。那么MarkWord在对象头中到底长什么样,也就是它到底存储了什么呢?

在32位的虚拟机中:

在64位的虚拟机中:

上图中的偏向锁和轻量级锁都是在java6以后对锁机制进行优化时引进的,下文的锁升级部分会具体讲解,Synchronized关键字对应的是重量级锁,接下来对重量级锁在Hotspot JVM中的实现锁讲解。

2:Synchronized在JVM中的实现原理

重量级锁对应的锁标志位是10,存储了指向重量级监视器锁的指针,在Hotspot中,对象的监视器(monitor)锁对象由ObjectMonitor对象实现(C++),其跟同步相关的数据结构如下:

ObjectMonitor() {
    _count        = 0; //用来记录该对象被线程获取锁的次数
    _waiters      = 0;
    _recursions   = 0; //锁的重入次数
    _owner        = NULL; //指向持有ObjectMonitor对象的线程
    _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
  }

光看这些数据结构对监视器锁的工作机制还是一头雾水,那么我们首先看一下线程在获取锁的几个状态的转换:

线程的生命周期存在5个状态,start、running、waiting、blocking和dead

对于一个synchronized修饰的方法(代码块)来说:

  • 当多个线程同时访问该方法,那么这些线程会先被放进_EntryList队列,此时线程处于blocking状态。
  • 当一个线程获取到了实例对象的监视器(monitor)锁,那么就可以进入running状态,执行方法,此时,ObjectMonitor对象的_owner指向当前线程,_count加1表示当前对象锁被一个线程获取
  • 当running状态的线程调用wait()方法,那么当前线程释放monitor对象,进入waiting状态,ObjectMonitor对象的_owner变为null,_count减1,同时线程进入_WaitSet队列,直到有线程调用notify()方法唤醒该线程,则该线程重新获取monitor对象进入_Owner区
  • 如果当前线程执行完毕,那么也释放monitor对象,进入waiting状态,ObjectMonitor对象的_owner变为null,_count减1

那么Synchronized修饰的代码块/方法如何获取monitor对象的呢?

在JVM规范里可以看到,不管是方法同步还是代码块同步都是基于进入和退出monitor对象来实现,然而二者在具体实现上又存在很大的区别。通过javap对class字节码文件反编译可以得到反编译后的代码。

(1)Synchronized修饰代码块:

Synchronized代码块同步在需要同步的代码块开始的位置插入monitorentry指令,在同步结束的位置或者异常出现的位置插入monitorexit指令;JVM要保证monitorentry和monitorexit都是成对出现的,任何对象都有一个monitor与之对应,当这个对象的monitor被持有以后,它将处于锁定状态。

例如,同步代码块如下:

public class SyncCodeBlock {
   public int i;
   public void syncTask(){
       synchronized (this){
           i++;
       }
   }
}

对同步代码块编译后的class字节码文件反编译,结果如下(仅保留方法部分的反编译内容):

  public void syncTask();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter  //注意此处,进入同步方法
         4: aload_0
         5: dup
         6: getfield      #2             // Field i:I
         9: iconst_1
        10: iadd
        11: putfield      #2            // Field i:I
        14: aload_1
        15: monitorexit   //注意此处,退出同步方法
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit //注意此处,退出同步方法
        22: aload_2
        23: athrow
        24: return
      Exception table:
      //省略其他字节码.......

可以看出同步方法块在进入代码块时插入了monitorentry语句,在退出代码块时插入了monitorexit语句,为了保证不论是正常执行完毕(第15行)还是异常跳出代码块(第21行)都能执行monitorexit语句,因此会出现两句monitorexit语句。

(2)Synchronized修饰方法:

Synchronized方法同步不再是通过插入monitorentry和monitorexit指令实现,而是由方法调用指令来读取运行时常量池中的ACC_SYNCHRONIZED标志隐式实现的,如果方法表结构(method_info Structure)中的ACC_SYNCHRONIZED标志被设置,那么线程在执行方法前会先去获取对象的monitor对象,如果获取成功则执行方法代码,执行完毕后释放monitor对象,如果monitor对象已经被其它线程获取,那么当前线程被阻塞。

同步方法代码如下:

public class SyncMethod {
   public int i;
   public synchronized void syncTask(){
           i++;
   }
}

对同步方法编译后的class字节码反编译,结果如下(仅保留方法部分的反编译内容):

public synchronized void syncTask();
    descriptor: ()V
    //方法标识ACC_PUBLIC代表public修饰,ACC_SYNCHRONIZED指明该方法为同步方法
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2                  // Field i:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field i:I
        10: return
      LineNumberTable:
        line 12: 0
        line 13: 10
}

可以看出方法开始和结束的地方都没有出现monitorentry和monitorexit指令,但是出现的ACC_SYNCHRONIZED标志位。

三、锁的优化

1、锁升级

锁的4中状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态(级别从低到高)

(1)偏向锁:

为什么要引入偏向锁?

  • 因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

偏向锁的升级:

  • 当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

偏向锁的取消:

  • 偏向锁是默认开启的,而且开始时间一般是比应用程序启动慢几秒,如果不想有这个延迟,那么可以使用-XX:BiasedLockingStartUpDelay=0;
  • 如果不想要偏向锁,那么可以通过-XX:-UseBiasedLocking = false来设置;

(2)轻量级锁

为什么要引入轻量级锁?

  • 轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。

轻量级锁什么时候升级为重量级锁?

  • 线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址;
  • 如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。
  • 但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。

注意:为了避免无用的自旋,轻量级锁一旦膨胀为重量级锁就不会再降级为轻量级锁了;偏向锁升级为轻量级锁也不能再降级为偏向锁。一句话就是锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。

(3)这几种锁的优缺点(偏向锁、轻量级锁、重量级锁)

2、锁粗化

  • 按理来说,同步块的作用范围应该尽可能小,仅在共享数据的实际作用域中才进行同步,这样做的目的是为了使需要同步的操作数量尽可能缩小,缩短阻塞时间,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。
  • 但是加锁解锁也需要消耗资源,如果存在一系列的连续加锁解锁操作,可能会导致不必要的性能损耗。
  • 锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁的加锁解锁操作。

3、锁消除

  • Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,经过逃逸分析,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java并发之synchronized实现原理深入理解

    目录 synchronized的三种应用方式 synchronized作用于实例方法 synchronized作用于静态方法 synchronized同步代码块 synchronized底层语义原理 理解Java对象头与Monitor synchronized代码块底层原理 synchronized方法底层原理 Java虚拟机对synchronized的优化 偏向锁 轻量级锁 自旋锁 锁消除 关于synchronized 可能需要了解的关键点 synchronized的可重入性 线程中断与syn

  • Java synchronized最细讲解

    目录 前言 Synchronization实现原理 先理解Java对象头与Monitor 1.对象头:锁的类型和状态和对象头的Mark Word息息相关: jdk6 之后做了改进,引入了偏向锁和轻量级锁: 1.无锁到偏向锁转化的过程 2.偏向锁升级轻量级 3.轻量级到重量级 总结 前言 线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据. 因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在

  • 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 使用方式 字节码语义 对象锁(monitor) 锁升级过程 为什么要用synchronized 相信大家对于这个问题一定都有自己的答案,这里我还是要啰嗦一下,我们来看下面这段车站售票的代码: /** * 车站开两个窗口同时售票 */ public class TicketDemo { public static void main(String[] args) { TrainStation station = new TrainStation(); //

  • Java synchronized锁升级jol过程详解

    jol(java object layout)需要的依赖 <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency> 一.synchronized锁对象的升级(膨胀)过程主要如下: 1.膨胀过程:无锁(锁对象初始化时)-> 偏向

  • JAVAsynchronized原理详解

    目录 1.synchronized的作用 2.synchronized的语法 3.Monitor原理 4.synchronized的原理 4.1偏向锁 4.2轻量级锁 4.3锁膨胀 4.4重量级锁 4.5自旋锁 4.6锁消除 4.7锁粗化 5.锁升级过程 总结 1.synchronized的作用 为了避免临界区的竞态条件发生,有多种手段可以达到目的. 阻塞式的解决方案:synchronized,Lock 非阻塞式的解决方案:原子变量 synchronized,即俗称的[对象锁],它采用互斥的方式

  • java中synchronized锁的升级过程

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

  • Java中synchronized锁升级的过程

    目录 简介 CAS markWord Synchronized的锁升级 偏向锁 轻量级锁 重量级锁 总结 简介 在多线程中解决线程安全的问题时常用到Synchronized,现在的synchronized相对于早期的synchronized做出了优化,从以前的加锁就是重量级锁优化成了有一个锁升级的过程(偏向锁->轻量级锁->重量级锁). CAS cas的全称是compare and swap,从名称上可以看出它是先比较再进行设置,它是一种在多线程环境下实现同步功能的机制. 下面这段代码是在Re

  • Java中synchronized关键字引出的多种锁 问题

    前言 Java 中的 synchronized关键字可以在多线程环境下用来作为线程安全的同步锁.本文不讨论 synchronized 的具体使用,而是研究下synchronized底层的锁机制,以及这些锁分别的优缺点. 一 synchronized机制 synchronized关键字是JAVA中常用的同步功能,提供了简单易用的锁功能. synchronized有三种用法,分别为: 用在普通方法上,能够锁住当前对象.用在静态方法上,能够锁住类用在代码块上,锁住的是synchronized()里的对

  • 详解Java中的锁Lock和synchronized

    一.Lock接口 1.Lock接口和synchronized内置锁 a)synchronized:Java提供的内置锁机制,Java中的每个对象都可以用作一个实现同步的锁(内置锁或者监视器Monitor),线程在进入同步代码块之前需要或者这把锁,在退出同步代码块会释放锁.而synchronized这种内置锁实际上是互斥的,即没把锁最多只能由一个线程持有. b)Lock接口:Lock接口提供了与synchronized相似的同步功能,和synchronized(隐式的获取和释放锁,主要体现在线程进

  • 透彻理解Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别

    本文讲述了Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别.分享给大家供大家参考,具体如下: Synchronized和Static Synchronized区别 通过分析这两个用法的分析,我们可以理解java中锁的概念.一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁).实例锁对应的就是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实现原理详解

    记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字"同步",也成为了我们解决多线程情况的百试不爽的良药.但是,随着我们学习的进行我们知道synchronized是一个重量级锁,相对于Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它. 诚然,随着Javs SE 1.6对synchronized进行的各种优化后,synchronized并不会显得那么

  • Java实现synchronized锁同步机制

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

  • Java中synchronized 的4个优化技巧

    目录 前言 1.锁膨胀 2.锁消除 3.锁粗化 4.自适应自旋锁 总结 前言 synchronized 在 JDK 1.5 时性能是比较低的,然而在后续的版本中经过各种优化迭代,它的性能也得到了前所未有的提升,上一篇文章我们谈到了锁膨胀对 synchronized 性能的提升,然而它也只是“众多” synchronized 性能优化方案中的一种,那么我们本文就来盘点一下 synchronized 的核心优化方案. synchronized 核心优化方案主要包含以下 4 个: 锁膨胀 锁消除 锁粗

  • java中synchronized(同步代码块和同步方法)详解及区别

     java中synchronized(同步代码块和同步方法)详解及区别 问题的由来: 看到这样一个面试题: //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized (obj){} } synchronized用于解决同步问题,当有多条线程同时访问共享数据时,如果进行同步,就会发生错误,Java提供的解决方案是:只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他

随机推荐