synchronized关键字实现原理

上篇文章中已经学习了synchronized关键字的作用,以及使用方式,这篇来学习一下synchronized的底层实现原理。


首先需要知道,synchronized对象锁的指针指向的是一个monitor对象(ObjectMonitor由C++实现的),每个对象都有对应的monitor,对象的monitor随对象的创建而创建,或随线程获取对象锁而创建,随销毁而销毁。我们需要关注的是ObjectMonitor中的几个关键属性

synchronized关键字实现原理_第1张图片

当多个线程同时访问一段同步代码块,每个线程都会被封装成ObjectWaiter对象,首先会被存入_EntryList队列中等待获取对象锁,当获取到对象锁后进入_owner中成为当前对象锁的持有者,此时_count会加1,持有锁后开始执行代码,执行完毕后此线程释放锁,_owner置为null,_count会减1。若在执行过程执行了wait操作,则此线程会被存放到_WaitSet中等待被唤醒,此时此线程也会释放锁,_owner也会置为null,_count也会减1。以便其他线程可以正常获取锁。


另外还需要知道,synchronized在修饰方法和修饰代码块时的实现原理不是一样的。


 synchronized修饰代码块的底层实现


首先写一个简单的同步代码块,编译后生成.class文件我们通过javap反编译来查看.class编译后生成的字节码。

可以参考https://www.cnblogs.com/frinder6/p/5440173.html了解javap,我也不太会⊙﹏⊙‖∣

package com.thread.demo;

/**
 * @Author: Peakcock__
 * @Date: 2019/4/28 14:24
 */
public class SynchronizedDemo {

    public void test(){
        synchronized (this){
            System.out.println("1111");
        }
    }

}

synchronized关键字实现原理_第2张图片

我们通过javap -c查看.class文件可以看出,被synchronized修饰的代码块,在执行前需要先执行monitorenter指令,在代码块结束后执行monitorexit指令,但是通过上图我们会很疑问,为什么执行了两次monitorexit呢?原因是每一个monitorenter指令都必须要有对应的monitorexit指令,但是谁也不能保证synchronized修饰的代码块一定会执行成功,所以编译器会默认自动生成异常处理器,捕获所有异常,捕获到异常后会执行monitorexit指令。

当执行monitorenter指令时,当前线程会尝试去获取同步对象的锁,当对象锁的monitor中的计数器(_count)为0时,当前线程会获取到对象锁,此时对象锁的monitor中的计数器会被设置为1,其他线程无法获取到对象锁。在当前线程执行完毕后,monitorexit指令会被执行,释放对象锁并将monitor的计数器置为0,让其他线程有机会获得对象锁。


synchronized在修饰方法时的底层实现


同上面一样,写一个简单的同步方法,编译后生成.class文件我们通过javap反编译来查看.class编译后生成的字节码。 

package com.thread.demo;

/**
 * @Author: Peakcock__
 * @Date: 2019/4/28 14:24
 */
public class SynchronizedDemo {

    public synchronized void test() {
        System.out.println("1111");
    }

}

synchronized关键字实现原理_第3张图片

可以看到,通过javap -v编译后的字节码文件中并没有monitorenter和monitorexit相关的指令,而是在flag处多了一个ACC_SYNCHRONIZED标识,表示此方法为同步方法,需要执行相应的同步调用。

你可能感兴趣的:(Java基础知识,Java关键字,Java,多线程)