Skip to content
目录概览

synchronized 底层实现原理?

  • 使用及原理 synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。示例代码:

    public class SynchronizedDemo
    {
        private Object object = new Object();
        public void m(){
            synchronized (object){
                System.out.println("123");
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    通过 javap -c -v SynchronizedDemo 可以得到以下字节码

    java
     0 aload_0
     1 getfield #3 <com/lupf/xxx/SynchronizedDemo.object>
     4 dup
     5 astore_1
     6 monitorenter
     7 getstatic #4 <java/lang/System.out>
    10 ldc #5 <123>
    12 invokevirtual #6 <java/io/PrintStream.println>
    15 aload_1
    16 monitorexit
    17 goto 25 (+8)
    20 astore_2
    21 aload_1
    22 monitorexit
    23 aload_2
    24 athrow
    25 return
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    前面的是monitorenter,后面的是离开monitorexit,不难想象一个线程也执行同步代码块,首先要获取锁,而获取锁的过程就是monitorenter ,在执行完代码块之后,要释放锁,释放锁就是执行monitorexit指令。

  • 为什么会有两个monitorexit呢? 这个主要是防止在同步代码块中线程因异常退出,而锁没有得到释放,这必然会造成死锁(等待的线程永远获取不到锁)。因此最后一个monitorexit是保证在异常情况下,锁也可以得到释放,避免死锁。 仅有ACC_SYNCHRONIZED这么一个标志,该标记表明线程进入该方法时,需要monitorenter,退出该方法时需要monitorexit。

  • synchronized可重入的原理 重入锁是指一个线程获取到该锁之后,该线程可以继续获得该锁。底层原理维护一个计数器,当线程获取该锁时,计数器加一,再次获得该锁时继续加一,释放锁时,计数器减一,当计数器值为0时,表明该锁未被任何线程所持有,其它线程可以竞争获取锁。

  • synchronized的优化 在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。

    但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。