Synchronized锁机制-java

synchronized是一种原子性内置锁,就是用C++写的,我们看不到其里面的源码,他就是一组指令,我们可以使用这个指令来实现对对象的加锁操作。

语法:(1)同步代码块;(2)同步方法

对象锁(monitor)机制:
(1)首先我们来观察使用synchronized对同步代码块加锁操作;

package Synchronized;

public class SynchronizedTest {
    
    
    public static Object object=new Object();

    public static void main(String[] args) {
    
    
        synchronized (object){
    
    
            System.out.println("hello baby");
        }
    }
}

经过反汇编以后,我们看到反汇编后的编码信息:
在这里插入图片描述
执行同步代码块后首先要先执行monitorenter指令,退出的时候monitorexit指令。通过分析之后可以看出,使用Synchronized进行同步,其关键就是必须要对对象的监视器monitor进行获取,当线程获取monitor后才能继续往下执行,否则就只能等待。而这个获取的过程是互斥的,即同一时刻只有一个线程能够获取到monitor。

在上述字节码中包含一个monitorenter指令和两个monitorexit指令,这是因为Java虚拟机需要确保所获得的锁在正常执行路径,以及异常执行路径上都能够被解锁。

(2)执行同步代码块的加锁编译后:
编译后的字节码文件中出现了这个标记:ACC_SYNCHRONIZED,该标记表示在进入该方法时,Java 虚拟机需要进行 monitorenter 操作,而在退出该方法时,不管是正常返回,还是向调用者抛异常,Java 虚拟机均需要进行 monitorexit 操作。(所以存在两次monitorexit退出操作)

public class SynchronizedTest {
    
    
    public static Object object=new Object();
    public  synchronized void eat(){
    
    
        System.out.println("eat banana");
    }

    public static void main(String[] args) {
    
    
        SynchronizedTest s=new SynchronizedTest();
        s.eat();
        }
   }

经过编译后的结果显示:
在这里插入图片描述

monitorenter 和 monitorexit 的作用:

我们可以抽象地理解为每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行 monitorenter 时,如果目标锁对象的计数器为 0,那么说明它没有被其他线程所持有。在这个情况下,Java 虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加 1。在目标锁对象的计数器不为 0 的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加 1,否则需要等待,直至持有线程释放该锁。
当执行 monitorexit 时,Java 虚拟机则需将锁对象的计数器减 1。当计数器减为 0 时,那便代表该锁已经被释放掉了。

之所以采用这种计数器的方式,是为了允许同一个线程重复获取同一把锁。举个例子,如果一个 Java 类中拥有多个 synchronized 方法,那么这些方法之间的相互调用,不管是直接的还是间接的,都会涉及对同一把锁的重复加锁操作。因此,我们需要设计这么一个可重入的特性,来避免编程里的隐式约束。

猜你喜欢

转载自blog.csdn.net/m0_46551861/article/details/115168611