欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java synchronized底层原理与底层锁优化

程序员文章站 2022-06-19 11:58:08
synchronized发生异常会自动释放锁在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的互斥锁来实现的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 J...

synchronized发生异常会自动释放锁

在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的互斥锁来实现的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方对从 JVM 层面对synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了。JDK1.6对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

使用方式

  • 修饰实例方法方法: 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁。synchronized(this)。
  • **修饰静态方法: ** 也就是给当前类加锁,会作用于类的所有对象实例。
    因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
  • **修饰代码块: ** 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。最好别用字符串,因为JVM中,字符串常量池具有缓存功能。

双重校验锁实现对象单例(线程安全):

通过volatile保证线程的可见性并防止指令的重新排序,synchronized加锁防止重复实例化。

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

synchronized 关键字的底层原理

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("synchronized 代码块");
        }
    }

    public synchronized void synchronizedMethod() {
        System.out.println("synchronized 方法");
    }
}

通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 javac SynchronizedDemo.java 命令生成编译后的 .class 文件,然后执行javap -c -s -v -l SynchronizedDemo.class。

synchronized代码块:

Java synchronized底层原理与底层锁优化

synchronized方法:
Java synchronized底层原理与底层锁优化

synchronized代码块

**synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。 **

当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因) 的持有权。当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。

monitor原理及执行步骤

因为每个对象头中都存在monitor锁对象,所以java中可以用对象加锁。

对象结构大概分为对象头、实例变量和填充字节。

  • 对象头
    • Mark Word(标记字段):存储对象的hashCode、锁信息或分代年龄或GC标志等信息
    • Klass Pointer(类型指针):对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
    • 数组长度:只有数组对象保存了这部分数据。该数据在32位和64位JVM中长度都是32bit。
  • 实例变量:实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。
  • 填充字节:第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

每一个锁都对应一个monitor对象,在HotSpot虚拟机中它是由ObjectMonitor实现的(C++实现)。每个对象都存在着一个monitor与之关联,对象与其monitor之间的关系有存在多种实现方式,查看部分ObjectMonitor.cpp源码:

ObjectMonitor () {
	// 指向持有ObjectMonitor对象的线程地址。
	_owner = NULL;

	//  存放调用wait方法,而进入等待状态的线程的队列。
	_WaitSet = NULL

	// 这里是等待锁block状态的线程的队列。
	_EntryList = NULL;

	// 锁的重入次数。
	_recursions = 0;

	// 线程获取锁的次数。
	_count = 0;
}

执行步骤:

  1. 当多个线程同时访问时,这些线程先被放进**_EntryList队列**中,线程处于blocked状态。
  2. 当其中的一个线程获取到对象的monitor后,该线程进入running状态,_owner指向当前线程,_count加1表示锁被一个线程获取到。
  3. 当running状态线程调用wait()方法,释放当前线程的monitor对,并进入waiting状态。_owner变为NULL,_count减1,将线程加入都_WaitSet队列中,当有线程notify()该线程时,将该线程加入到_EntryList队列中参与锁的竞争
  4. 当线程执行完成时,释放monitor对象,_owner为NULL,_count减1

synchronized方法

方法头部使用的是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

JDK1.6之后synchronized优化

JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。

如果不了解,请点击学习:Java中的锁膨胀、锁消除、锁粗化、自旋锁、偏向锁、轻量级锁、重量级锁详解

本文地址:https://blog.csdn.net/weixin_41590779/article/details/107618780