精华内容
下载资源
问答
  • 锁膨胀过程

    2019-08-12 15:47:26
    锁膨胀过程 锁膨胀方向:无锁 —> 偏向锁 —> 轻量级锁 —> 重量级锁 注:我们之前经常会听到锁升级是不允许降级的,这指的是 ”偏向锁 —> 轻量级锁 —> 重量级锁“ 这个过程,不包括 ”无锁...

    锁膨胀过程

    	锁膨胀方向:无锁 —> 偏向锁 —> 轻量级锁 —> 重量级锁
    	
    	注:我们之前经常会听到锁升级是不允许降级的,这指的是 ”偏向锁 —> 轻量级锁 —> 重量级锁“ 这个过程
    	不包括 ”无锁 —> 偏向锁 “,当然你也可以这么理解,无锁状态本身就没有锁,所以何来锁升级一说呢?
    

    1.从无锁到偏向锁

    一个对象刚开始实例化的时候,没有任何线程来访问它的时候。它是可偏向的,意味着,它现在认为只可能有一个线程来访问它,所以当第一个线程来访问它的时候,它会偏向这个线程,此时,对象持有偏向锁,偏向第一个线程。这个线程在修改对象头成为偏向锁的时候使用 CAS 操作,并将对象头中的 ThreadID 改成自己的 ID,之后再次访问这个对象时,只需要对比 ID,不需要再使用 CAS.

    2.从偏向锁到轻量级锁

    一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象是偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁( 偏向锁就是这个时候升级为轻量级锁的)。如果不存在使用了,则可以将对象回复成无锁状态,然后重新偏向。

    3.从轻量级锁到重量级锁

    轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止 CPU 空转

    展开全文
  • syntronized锁膨胀过程

    2020-08-01 15:21:34
    这几天在逛B站时看别人的面试过程,问到了syntronized的锁膨胀过程,突然发现自己对这块不是很了解,在网上收集了一些资料记录一下: 首先:为什么要有锁膨胀过程: 在java 1.6之前,syntronized的操作是一项非常...

    这几天在逛B站时看别人的面试过程,问到了syntronized的锁膨胀过程,突然发现自己对这块不是很了解,在网上收集了一些资料记录一下:

    首先:为什么要有锁膨胀过程:

    在java 1.6之前,syntronized的操作是一项非常重的操作,属于重量级锁,但是Java的开发者们发现其实很多时候锁的竞争机制不是那么激烈的,没有必要在一个同步块中采取这样消耗性能的方式来处理,因为资源的竞争,加锁解锁,涉及到了用户态和内核态的转换。

    所以在Java1.6的时候,他们对这个关键字进行了一些优化,这就是syntronized的锁膨胀过程。

    那么,什么是syntronized的锁膨胀过程?

    suotronized的锁膨胀过程为:无锁-》偏向锁-》轻量级锁-》重量级锁,膨胀的过程也正是syntronized的优化点,它可以根据资源竞争的程度来进行膨胀,锁膨胀过程不可逆,但是偏向锁可以被重置为无锁状态。

    说到锁膨胀过程,就不得不提一下markword,他是在对象头中的一块内存区域,专门用于记录当前对象的锁状态的。

    很多时候,我们说各种锁,其实他们的本质还是一块内存区域,简单理解:将markword中的标记位+1,就代表这个对象上锁,-1,代表释放锁,如果当前状态为0,说明没有上锁。

    回到我们刚刚说的锁膨胀过程:

    无锁过程:没有线程执行同步方法,代码块时候的状态

    偏向锁:锁资源可以由同一个线程多次获得,降低了获取锁时候的性能开销,针对不存在锁竞争时候的情况

    轻量级锁:通过cas操作markword中的标志位来实现,其中cas自选分为固定次数自旋和自适应自旋,达到自选阈值之后就转为重量级锁。它可以应用于竞争锁对象线程不多且持有锁时间不长的场景。

    重量级锁:通过内部监视器monitor实现,monitor的本质是基于操作系统互斥mutex机制来实现的。

    可以看到,它可以自适应地根据当前的锁竞争情况来自动调节锁的类型和程度,这样就减少了性能上的开销。

    另外还有一点值得注意的是,在锁膨胀过程,其实是一个不可逆的过程,但是比较特殊的是,无锁状态和偏向锁状态是可以切换的。

    展开全文
  • 深入分析synchronized原理和锁膨胀过程(二)

    万次阅读 多人点赞 2019-03-22 23:25:04
    文章目录前言synchronized实现原理同步代码块同步方法两种同步方式区别Java对象的组成对象头实例数据对齐填充synchronized锁膨胀过程锁的优化自旋锁和自适应性自旋锁锁消除锁粗化轻量级锁偏向锁锁的转换过程三种锁的...
    image
    

    前言

    上一篇文章介绍了多线程的概念及synchronized的使用方法《synchronized的使用(一)》,但是仅仅会用还是不够的,只有了解其底层实现才能在开发过程中运筹帷幄,所以本篇探讨synchronized的实现原理及锁升级(膨胀)的过程。

    synchronized实现原理

    synchronized是依赖于JVM来实现同步的,在同步方法和代码块的原理有点区别。

    同步代码块

    我们在代码块加上synchronized关键字

    public void synSay() {
        synchronized (object) {
            System.out.println("synSay----" + Thread.currentThread().getName());
        }
    }
    

    编译之后,我们利用反编译命令javap -v xxx.class查看对应的字节码,这里为了减少篇幅,我就只粘贴对应的方法的字节码。

      public void synSay();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=3, locals=3, args_size=1
             0: aload_0
             1: getfield      #2                  // Field object:Ljava/lang/String;
             4: dup
             5: astore_1
             6: monitorenter
             7: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
            10: new           #4                  // class java/lang/StringBuilder
            13: dup
            14: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
            17: ldc           #6                  // String synSay----
            19: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            22: invokestatic  #8                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
            25: invokevirtual #9                  // Method java/lang/Thread.getName:()Ljava/lang/String;
            28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            31: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            34: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            37: aload_1
            38: monitorexit
            39: goto          47
            42: astore_2
            43: aload_1
            44: monitorexit
            45: aload_2
            46: athrow
            47: return
          Exception table:
             from    to  target type
                 7    39    42   any
                42    45    42   any
          LineNumberTable:
            line 21: 0
            line 22: 7
            line 23: 37
            line 24: 47
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      48     0  this   Lcn/T1;
          StackMapTable: number_of_entries = 2
            frame_type = 255 /* full_frame */
              offset_delta = 42
              locals = [ class cn/T1, class java/lang/Object ]
              stack = [ class java/lang/Throwable ]
            frame_type = 250 /* chop */
              offset_delta = 4
    

    可以发现synchronized同步代码块是通过加monitorentermonitorexit指令实现的。
    每个对象都有个**监视器锁(monitor) **,当monitor被占用的时候就代表对象处于锁定状态,而monitorenter指令的作用就是获取monitor的所有权,monitorexit的作用是释放monitor的所有权,这两者的工作流程如下:
    monitorenter

    1. 如果monitor的进入数为0,则线程进入到monitor,然后将进入数设置为1,该线程称为monitor的所有者。
    2. 如果是线程已经拥有此monitor(即monitor进入数不为0),然后该线程又重新进入monitor,则将monitor的进入数+1,这个即为锁的重入
    3. 如果其他线程已经占用了monitor,则该线程进入到阻塞状态,知道monitor的进入数为0,该线程再去重新尝试获取monitor的所有权

    monitorexit:执行该指令的线程必须是monitor的所有者,指令执行时,monitor进入数-1,如果-1后进入数为0,那么线程退出monitor,不再是这个monitor的所有者。这个时候其它阻塞的线程可以尝试获取monitor的所有权。

    同步方法

    在方法上加上synchronized关键字

    synchronized public void synSay() {
        System.out.println("synSay----" + Thread.currentThread().getName());
    }
    

    编译之后,我们利用反编译命令javap -v xxx.class查看对应的字节码,这里为了减少篇幅,我就只粘贴对应的方法的字节码。

      public synchronized void synSay();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_SYNCHRONIZED
        Code:
          stack=3, locals=1, args_size=1
             0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: new           #3                  // class java/lang/StringBuilder
             6: dup
             7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
            10: ldc           #5                  // String synSay----
            12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            15: invokestatic  #7                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
            18: invokevirtual #8                  // Method java/lang/Thread.getName:()Ljava/lang/String;
            21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            24: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            27: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            30: return
          LineNumberTable:
            line 20: 0
            line 21: 30
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      31     0  this   Lcn/T1;
    

    从字节码上看,加有synchronized关键字的方法,常量池中比普通的方法多了个ACC_SYNCHRONIZED标识,JVM就是根据这个标识来实现方法的同步。
    当调用方法的时候,调用指令会检查方法是否有ACC_SYNCHRONIZED标识,有的话线程需要先获取monitor,获取成功才能继续执行方法,方法执行完毕之后,线程再释放monitor,同一个monitor同一时刻只能被一个线程拥有。

    两种同步方式区别

    synchronized同步代码块的时候通过加入字节码monitorentermonitorexit指令来实现monitor的获取和释放,也就是需要JVM通过字节码显式的去获取和释放monitor实现同步,而synchronized同步方法的时候,没有使用这两个指令,而是检查方法的ACC_SYNCHRONIZED标志是否被设置,如果设置了则线程需要先去获取monitor,执行完毕了线程再释放monitor,也就是不需要JVM去显式的实现。
    这两个同步方式实际都是通过获取monitor和释放monitor来实现同步的,而monitor的实现依赖于底层操作系统的mutex互斥原语,而操作系统实现线程之间的切换的时候需要从用户态转到内核态,这个转成过程开销比较大。
    线程获取、释放monitor的过程如下:

    线程尝试获取monitor的所有权,如果获取失败说明monitor被其他线程占用,则将线程加入到的同步队列中,等待其他线程释放monitor当其他线程释放monitor后,有可能刚好有线程来获取monitor的所有权,那么系统会将monitor的所有权给这个线程,而不会去唤醒同步队列的第一个节点去获取,所以synchronized是非公平锁。如果线程获取monitor成功则进入到monitor中,并且将其进入数+1

    关于什么是公平锁、非公平锁可以参考一下美团技术团队写的《不可不说的Java“锁”事》

    到这里我们也清楚了synchronized的语义底层是通过一个monitor的对象完成,其实waitnotiyfnotifyAll等方法也是依赖于monitor对象来完成的,这也就是为什么需要在同步方法或者同步代码块中调用的原因(需要先获取对象的锁,才能执行),否则会抛出java.lang.IllegalMonitorStateException的异常

    Java对象的组成

    我们知道了线程要访问同步方法、代码块的时候,首先需要取得锁,在退出或者抛出异常的时候又必须释放锁,那么锁到底是什么?又储存在哪里?
    为了解开这个疑问,我们需要进入Java虚拟机(JVM) 的世界。在HotSpot虚拟机中,Java对象在内存中储存的布局可以分为3块区域:对象头实例数据对齐填充synchronized使用的锁对象储存在对象头中

    对象头

    对象头的数据长度在32位和64位(未开启压缩指针)的虚拟机中分别为32bit64bit。对象头由以下三个部分组成:

    • Mark Word:记录了对象和锁的有关信息,储存对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁标志位、线程持有的锁、偏向线程ID、偏向时间戳、对象分代年龄等。注意这个Mark Word结构并不是固定的,它会随着锁状态标志的变化而变化,而且里面的数据也会随着锁状态标志的变化而变化,这样做的目的是为了节省空间
    • 类型指针:指向对象的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
    • 数组长度:这个属性只有数组对象才有,储存着数组对象的长度。

    32位虚拟机下,Mark Word的结构和数据可能为以下5种中的一种。

    64位虚拟机下,Mark Word的结构和数据可能为以下2种中的一种。

    这里重点注意是否偏向锁锁标志位,这两个标识和synchronized的锁膨胀息息相关。

    实例数据

    储存着对象的实际数据,也就是我们在程序中定义的各种类型的字段内容。

    对齐填充

    HotSpot虚拟机的对齐方式为8字节对齐,即一个对象必须为8字节的整数倍,如果不是,则通过这个对齐填充来占位填充。

    synchronized锁膨胀过程

    上文介绍的 “synchronized实现原理” 实际是synchronized实现重量级锁的原理,那么上文频繁提到monitor对象和对象又存在什么关系呢,或者说monitor对象储存在对象的哪个地方呢?
    在对象的对象头中,当锁的状态为重量级锁的时候,它的指针即指向monitor对象,如图:



    那锁的状态为其它状态的时候是不是就没用上monitor对象?答案:是的。
    这也是JVMsynchronized的优化,我们知道重量级锁的实现是基于底层操作系统的mutex互斥原语的,这个开销是很大的。所以JVMsynchronized做了优化,JVM先利用对象头实现锁的功能,如果线程的竞争过大则会将锁升级(膨胀)为重量级锁,也就是使用monitor对象。当然JVM对锁的优化不仅仅只有这个,还有引入适应性自旋、锁消除、锁粗化、轻量级锁、偏向锁等。

    那么锁的是怎么进行膨胀的或者依据什么来膨胀,这也就是本篇需要介绍的重点,首先我们需要了解几个概念。

    锁的优化

    自旋锁和自适应性自旋锁

    自旋:当有个线程A去请求某个锁的时候,这个锁正在被其它线程占用,但是线程A并不会马上进入阻塞状态,而是循环请求锁(自旋)。这样做的目的是因为很多时候持有锁的线程会很快释放锁的,线程A可以尝试一直请求锁,没必要被挂起放弃CPU时间片,因为线程被挂起然后到唤醒这个过程开销很大,当然如果线程A自旋指定的时间还没有获得锁,仍然会被挂起。

    自适应性自旋:自适应性自旋是自旋的升级、优化,自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态决定。例如线程如果自旋成功了,那么下次自旋的次数会增多,因为JVM认为既然上次成功了,那么这次自旋也很有可能成功,那么它会允许自旋的次数更多。反之,如果对于某个锁,自旋很少成功,那么在以后获取这个锁的时候,自旋的次数会变少甚至忽略,避免浪费处理器资源。有了自适应性自旋,随着程序运行和性能监控信息的不断完善,JVM对程序锁的状况预测就会变得越来越准确,JVM也就变得越来越聪明。

    锁消除

    锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除

    锁粗化

    在使用锁的时候,需要让同步块的作用范围尽可能小,这样做的目的是为了使需要同步的操作数量尽可能小,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁

    轻量级锁

    所谓轻量级锁是相对于使用底层操作系统mutex互斥原语实现同步的重量级锁而言的,因为轻量级锁同步的实现是基于对象头的Mark Word。那么轻量级锁是怎么使用对象头来实现同步的呢,我们看看具体实现过程。

    获取锁过程

    1. 在线程进入同步方法、同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为"01"状态,是否为偏向锁为"0"),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Recored)的空间,用于储存锁对象目前的Mark Word的拷贝(官方把这份拷贝加了个Displaced前缀,即Displaced Mark Word)。

    1. 将对象头的Mark Word拷贝到线程的锁记录(Lock Recored)中。
    2. 拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针。如果这个更新成功了,则执行步骤4,否则执行步骤5
    3. 更新成功,这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位将转变为"00",即表示此对象处于轻量级锁的状态。

    1. 更新失败,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,可以直接进入同步块继续执行,否则说明这个锁对象已经被其其它线程抢占了。进行自旋执行步骤3,如果自旋结束仍然没有获得锁,轻量级锁就需要膨胀为重量级锁,锁标志位状态值变为"10",Mark Word中储存就是指向monitor对象的指针,当前线程以及后面等待锁的线程也要进入阻塞状态。

    释放锁的过程

    1. 使用CAS操作将对象当前的Mark Word和线程中复制的Displaced Mark Word替换回来(依据Mark Word中锁记录指针是否还指向本线程的锁记录),如果替换成功,则执行步骤2,否则执行步骤3
    2. 如果替换成功,整个同步过程就完成了,恢复到无锁的状态(01)。
    3. 如果替换失败,说明有其他线程尝试获取该锁(此时锁已膨胀),那就要在释放锁的同时,唤醒被挂起的线程。

    偏向锁

    偏向锁的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用CAS操作区消除同步使用的互斥量,那么偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不用做了。偏向锁默认是开启的,也可以关闭
    偏向锁"偏",就是"偏心"的"偏",它的意思是这个锁会偏向于第一个获得它的程序,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。

    获取锁的过程

    1. 检查Mark Word是否为可偏向锁的状态,即是否偏向锁即为1即表示支持可偏向锁,否则为0表示不支持可偏向锁。
    2. 如果是可偏向锁,则检查Mark Word储存的线程ID是否为当前线程ID,如果是则执行同步块,否则执行步骤3
    3. 如果检查到Mark WordID不是本线程的ID,则通过CAS操作去修改线程ID修改成本线程的ID,如果修改成功则执行同步代码块,否则执行步骤4
    4. 当拥有该锁的线程到达安全点之后,挂起这个线程,升级为轻量级锁。

    锁释放的过程

    1. 有其他线程来获取这个锁,偏向锁的释放采用了一种只有竞争才会释放锁的机制,线程是不会主动去释放偏向锁,需要等待其他线程来竞争。
    2. 等待全局安全点(在这个是时间点上没有字节码正在执行)。
    3. 暂停拥有偏向锁的线程,检查持有偏向锁的线程是否活着,如果不处于活动状态,则将对象头设置为无锁状态,否则设置为被锁定状态。如果锁对象处于无锁状态,则恢复到无锁状态(01),以允许其他线程竞争,如果锁对象处于锁定状态,则挂起持有偏向锁的线程,并将对象头Mark Word的锁记录指针改成当前线程的锁记录,锁升级为轻量级锁状态(00)

    锁的转换过程

    锁主要存在4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争的情况逐渐升级,这几个锁只有重量级锁是需要使用操作系统底层mutex互斥原语来实现,其他的锁都是使用对象头来实现的。需要注意锁可以升级,但是不可以降级。


    这里盗个图,这个图总结的挺好的!

    在这里插入图片描述

    三种锁的优缺点比较

    image

    参考

    深入理解Java虚拟机
    Java的对象头和对象组成详解
    JVM(三)JVM中对象的内存布局详解
    JVM——深入分析对象的内存布局
    啃碎并发(七):深入分析Synchronized原理
    Java Synchronized实现原理

    原文地址:https://ddnd.cn/2019/03/22/java-synchronized-2/

    展开全文
  • 例子: 多线程请求示例: (工具) synchronized是隐式锁。 那它内部怎么实现? 对象锁: (对象头) ...新一轮锁竞争又是一个锁膨胀过程...

    例子:

    多线程请求示例:

    (工具)

     

     

     

    synchronized是隐式锁。

    那它内部怎么实现?

     

    对象锁:

    (对象头)

     

     

     

    对象的MarkWord,对象头,对象锁状态

     

    轻量级锁升级过程:

     

    新一轮锁竞争又是一个锁膨胀过程(无锁,偏向锁,轻量级锁,重量级锁)

    线程挂起,会有 线程的上下文切换

     

     

     

    深度理解参考:

    深入分析synchronized原理和锁膨胀过程(二)

     

     

    展开全文
  • 【死磕 Java 并发】—– synchronized 的锁膨胀过程 synchronized 是 Java 面试的常客,我们需要掌握它的基本使用,比如同步代码块、同步普通方法、同步静态方法,以及他们的区别,当然这是最初级的。高级点的...
  • 高级点的就是需要掌握 synchronized 的实现原理,比如对象头、synchronized 的锁优化、锁的膨胀过程,这篇文章就是介绍 synchronized 的锁膨胀过程。该过程其实很多小伙伴都不知道,18年面试的时候问了不下于 20 ...
  • 文章目录前言正文轻量...这句话看起来很简单,但实际上synhronized的膨胀过程是非常复杂的,有许多场景和细节需要考虑,本篇就对其进行详细分析。 正文 先来看一个案例代码: public class TestInflate { static Th
  • 高级点的就是需要掌握 synchronized 的实现原理,比如对象头、synchronized 的锁优化、锁的膨胀过程,这篇文章就是介绍 synchronized 的锁膨胀过程。该过程其实很多小伙伴都不知道,18年面试的时候问了不下于 20 ...
  • 在JDK 1.6之前,synchonized同步方式的...但是我刚接触的时候一直很纠结膨胀过程,后来搞明白了,现在抽个时间总结记录一下。 首先我们要知道,这几个级别适用的情况。 偏向:适用于只有一个线程进入同步区 轻量...
  • 大家好,我是烤鸭: 前几天看马士兵老师的并发的课,里边讲到了 synchronize 膨胀过程,今天想用代码演示一下。 1. 编写代码
  • 邀请好朋友小黄来家里庆祝,他们喝了牛奶,吃了巧克力,饭后,两个好朋友在沙发看电视,突然……小黑把小黄带到了卫生间,小黄惊呆了,原来,最近小黑在学习Java并发编程,总是搞不清synchronized膨胀,于是制作...
  • 前言上一篇分析了优化后的synchronized...这句话看起来很简单,但实际上synhronized的膨胀过程是非常复杂的,有许多场景和细节需要考虑,本篇就对其进行详细分析。正文先来看一个案例代码:这里创建了三个线程t1、t2...
  • 小黑把小黄带到了卫生间,小黄惊呆了,原来,最近小黑在学习Java并发编程,总是搞不清synchronized膨胀,于是制作了这个智能门帮助自己理解。 小黑也没有使用过这个卫生间,正好小黄要用,就来看看这个门有多...
  • 使用jol查看对象头内存布局(查看标志时,注意大端存储和小端存储) 引入依赖 <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <...
  • 三、锁膨胀升级过程 在 Java 的早期版本中,synchronized 会直接去获取 Monitor 的所有权而 Monitor 又属于重量级锁,这需要让线程从用户态转变为内核态而这个状态转换需要花费很多的处理器时间,甚至可能比用户...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 242
精华内容 96
关键字:

锁膨胀过程