synchronized底层原理

synchronized底层原理

通过反编译class文件,可以看到synchronized最关键的部分是monitor对象。

又因为synchronized关键字使用的方法不同,可以将monitor对象使用分为以下两种情况。

  • synchronized放在方法签名上

    public synchronized void method(){
        
    }
    

    这时候在反编译文件里,该方法的ACC_SYNCHRONIZED访问标志位会被标记

  • synchronized作为对象锁

    public void method(){
      synchronized(new Object()){
       
      }
    }
    

这时候ACC_SYNCHRONIZED并不会被标记,但是会执行monitorenter和monitorexit命令,从而实现同步。

  • 可以看到虽然只有一个monitorenter但是有两个monitorexit,这是因为有两种情况可以让当前线程放弃锁,即
    • 当前synchronized代码块运行完
    • 发生中断

monitor对象

其实以上两种方法都是相同的,ACC_SYNCHRONIZED标志位是隐式调用了monitor对象而已。下面来说一下monitor对象以及它是如何实现运作的。

monitor的数据结构

关于存储的monitor对象有以下三种可能,对应不同的synchronzied使用方式,

  • synchronzied修饰普通方法——>锁的是this,也就是调用当前方法的实例对象

  • synchronized修饰代码块——>锁的是synchronized后面括号里的方法

  • synchronized修饰static方法——>锁的是类的.class对象

    关于面试的时候会问的各种情况下会不会同步执行,牢记一点

    锁的对象相同才会同步执行

    锁的对象相同才会同步执行

    锁的对象相同才会同步执行

    也就是说,类锁对普通方法锁是不存在覆盖的,下面两个方法不会同步执行

    public synchronized void method1(){
            
        }
        public  static synchronized void method2(){
            
        }
    

monitor运行机制

如果一个线程运行到一个同步代码块,如果线程进入数为0,则该线程可拥有此monitor对象的锁,遇到monitorenter,进入数+1,遇到monitorexit,进入数-1。

如果目前线程进入数不为0,则当前线程不能获得此monitor对象的锁,需要等待。

synchronized的可重入性,不可中断性是如何保障的呢?

monitor对象的线程进入数不是0和1,如果发生重入,进入数+1即可。

不可中断性:一个线程获取锁后,其他线程必须阻塞或等待,不能抢占,按照上面的运行机制,必须线程进入数=0其他线程才能获得锁,因而,不可中断性实现。

另外,有的面试题会问非同步方法和同步方法同时调用会不会同步执行,答案是不会。

因为,非同步方法执行时不会去考虑线程进入数以及获得锁一系列流程,直接开始执行,怎么可能同步执行呢