synchronized 的底层原理

tip: 作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。

推荐:体系化学习Java(Java面试专题)

一、synchronized 的底层原理

synchronized 的底层是通过 Java 中的**监视器锁(monitor)**来实现的。每个 Java 对象都有一个与之对应的监视器锁,当一个线程获取了该对象的监视器锁,就可以执行 synchronized 代码块或方法。其他线程只能等待该线程释放锁,才能获取该对象的监视器锁并执行 synchronized 代码块或方法。

在 Java 中,每个对象都有一个与之关联的管程(monitor)。管程包括两个部分:一个是对象头(Object Header),用于存储对象的元数据信息;另一个是监视器锁(Monitor Lock),用于实现线程同步。当一个线程获取了对象的监视器锁,就可以执行 synchronized 代码块或方法,其他线程只能等待该线程释放锁,才能获取该对象的监视器锁并执行 synchronized 代码块或方法。

当一个线程进入 synchronized 代码块或方法时,它会尝试获取对象的监视器锁。如果该锁没有被其他线程占用,则该线程获取锁并继续执行 synchronized 代码块或方法;如果该锁已经被其他线程占用,则该线程会进入锁池(Lock Pool)等待,直到该锁被其他线程释放。

当一个线程释放对象的监视器锁时,它会唤醒锁池中的一个等待线程,让其获取锁并继续执行 synchronized 代码块或方法。如果锁池中有多个等待线程,那么唤醒哪个线程是不确定的,取决于 JVM 的实现。

二、synchronized 的锁升级原理

synchronized 的锁升级指的是在不同的情况下,synchronized 锁的状态会从偏向锁、轻量级锁、重量级锁等级别逐步升级的过程。在 Java 6 及之前的版本中,synchronized 的锁升级过程是固定的,而在 Java 6 及之后的版本中,锁升级过程是根据当前锁的状态和竞争情况动态调整的。

偏向锁:当一个线程访问同步块并获取锁时,会在对象头中记录锁偏向的线程 ID,以后该线程再次进入同步块时,只需判断当前线程 ID 是否与对象头中记录的线程 ID 相同,如果相同,就可以直接进入同步块,无需进行额外的同步操作。如果有其他线程竞争锁,则偏向锁会被撤销。

轻量级锁:当一个线程获取锁失败时,会尝试使用轻量级锁来提高性能。轻量级锁是通过将对象头中的信息复制到线程的栈帧中,然后在栈帧中进行同步操作来实现的。如果在同步过程中发生竞争,则轻量级锁会升级为重量级锁。

重量级锁:当多个线程竞争同一个锁时,会升级为重量级锁。重量级锁是通过操作系统的互斥量来实现的,每次加锁和释放锁都需要进行系统调用,开销较大。

在 Java 6 及之前的版本中,锁升级过程是固定的,即从偏向锁升级到轻量级锁,再升级到重量级锁。而在 Java 6 及之后的版本中,锁升级过程是根据当前锁的状态和竞争情况动态调整的,可以根据实际情况选择偏向锁、轻量级锁或重量级锁,从而提高程序的性能。

1、偏向锁

偏向锁是 Java 中的一种锁优化机制。它的主要目的是减少无竞争情况下的同步操作,从而提高程序的性能。在偏向锁的情况下,当一个线程访问同步块并获取锁时,会在对象头中记录锁偏向的线程 ID,以后该线程再次进入同步块时,只需判断当前线程 ID 是否与对象头中记录的线程 ID 相同,如果相同,就可以直接进入同步块,无需进行额外的同步操作。

偏向锁的优点是可以减少同步操作的开销,尤其是在无竞争的情况下。在无竞争的情况下,偏向锁可以避免多余的同步操作,从而提高程序的性能。另外,偏向锁的实现比较简单,开销也比较小。

偏向锁的缺点是当有其他线程竞争锁时,偏向锁会被撤销,这样就会导致额外的同步操作,从而降低程序的性能。此外,偏向锁只适用于无竞争的情况,如果存在竞争,就需要使用其他的锁机制来保证线程安全。

需要注意的是,偏向锁只适用于单线程访问同步块的情况。如果多个线程访问同一个同步块,就会出现竞争,此时偏向锁会被撤销,从而降低程序的性能。

package com.pany.camp.lock;

import org.openjdk.jol.info.ClassLayout;

public class BiasedLockDemo {

    public static void main(String[] args) throws InterruptedException {
        BiasedLockDemo biasedLockDemo = new BiasedLockDemo();
        biasedLockDemo.doSomething();
        System.out.println(ClassLayout.parseInstance(biasedLockDemo).toPrintable());
    }

    public synchronized void doSomething() {
        System.out.println("doSomething() is called");
    }
}

在这里插入图片描述
在上面的输出中,我们可以看到对象头中的第一个字节是 01 ,这个字节的二进制表示是 00000001 ,其中第0位是1,表示这个对象的锁是偏向锁。如果第0位是0,表示这个对象的锁不是偏向锁。

可以通过 -xx:UseBiasedLocking 禁用偏向锁。

2、轻量级锁

轻量级锁是Java虚拟机为了提高多线程程序性能而引入的一种锁优化机制。与传统的重量级锁相比,轻量级锁的锁操作使用了更加轻量级的机制,避免了不必要的线程阻塞和上下文切换,从而提高了多线程程序的执行效率。

轻量级锁的实现原理是,当一个线程第一次进入同步代码块时,Java虚拟机会为该对象分配一个锁记录(Lock Record)并将对象头中的Mark Word复制到锁记录中。然后,虚拟机会尝试使用CAS操作将对象头中的Mark Word替换为指向锁记录的指针,从而将对象的锁状态升级为轻量级锁。如果CAS操作成功,该线程就可以继续执行同步代码块,否则就会退化为重量级锁,从而保证线程安全。

当一个线程持有轻量级锁时,其他线程可以使用CAS操作来尝试获取锁。如果获取成功,就可以继续执行同步代码块。如果获取失败,就会进入自旋状态,等待持有锁的线程释放锁。如果自旋次数超过一定阈值,或者持有锁的线程被阻塞,就会退化为重量级锁。

总之,轻量级锁是Java虚拟机为了提高多线程程序性能而引入的一种锁优化机制,它避免了不必要的线程阻塞和上下文切换,从而提高了多线程程序的执行效率。

package com.pany.camp.lock;

import cn.hutool.core.thread.ThreadUtil;
import org.openjdk.jol.info.ClassLayout;

public class LightLock {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public static void main(String[] args) {
        final LightLock obj = new LightLock();
        final int numThreads = 5;
        final int numIterations = 10000;

        Thread t1 = new Thread(() -> {
            ThreadUtil.sleep(1000);
            System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        }, "t1");
        t1.start();

        // 创建多个线程并启动
        Thread[] threads = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < numIterations; j++) {
                    // 对共享对象加锁
                    synchronized (obj) {
                        obj.increment();
                        ThreadUtil.sleep(1);
                    }
                }
            });
            threads[i].start();
        }


        System.out.println("Count: " + obj.count);
    }
}


在这里插入图片描述
上面的例子,对象头的 Mark Word 已经替换为指向锁记录的指针,已经升级成轻量级锁了。

3、重量级锁

重量级锁是Java中的一种锁机制,也称为互斥锁。它是基于操作系统的互斥量实现的,需要进行用户态与内核态之间的切换,因此开销比较大,效率较低。当多个线程竞争同一个锁时,如果该锁是重量级锁,那么线程会进入阻塞状态,等待锁的释放。在Java中,synchronized关键字使用的锁就是重量级锁。

重量级锁的实现原理是,在Java虚拟机中,每个对象都与一个监视器关联,当一个线程需要获取该对象的锁时,它会尝试获取该对象的监视器锁。如果该锁已经被其他线程持有,那么该线程就会进入阻塞状态,等待锁的释放。当该锁被释放时,等待队列中的线程会被唤醒,并重新尝试获取锁。

重量级锁的优点是实现简单,可靠性高,不会出现死锁等问题。缺点是由于需要进行用户态与内核态之间的切换,因此开销比较大,效率较低。在多线程环境下,如果使用重量级锁过多,会导致系统性能下降,因此应该根据具体情况选择合适的锁机制。

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: "synchronized" 关键字的底层原理是基于 Java 的内置锁(Monitor)机制。 Java 中的锁是通过对象的 monitor 实现的,每个对象都有一个对应的 monitor。当线程进入一个对象的 synchronized 代码块时,它会请求该对象的 monitor,如果该 monitor 没有被其他线程占用,则该线程获得该 monitor 并执行代码块,否则该线程阻塞,直到该 monitor 被释放。 通过使用 monitor,Java 可以保证同一时刻只有一个线程能够访问该对象的 synchronized 代码块,从而实现线程同步。 因此,"synchronized" 关键字实现了线程同步的功能,并且可以避免因竞争访问共享资源导致的线程安全问题。 ### 回答2: synchronizedJava中的关键字,用于实现多线程同步。它的底层原理是通过对象的内部锁(也称为监视器锁)来实现线程的互斥访问。 在Java中,每个对象都有一个与之关联的内部锁。当一个线程尝试进入被synchronized修饰的代码块时,它会首先尝试获得该对象的内部锁。如果锁没有被其他线程所占用,那么该线程就会获取到锁,并且进入临界区执行代码。如果锁已经被其他线程所占用,那么该线程就会进入阻塞状态,直到锁被释放。 在synchronized的实现中,锁的状态有两种:被线程占用和未被占用。当一个线程获得锁后,它会将锁的状态设置为已被占用。其他线程在尝试获取该锁时,会发现锁已被占用,它们会进入锁的等待队列中,等待获取锁的线程释放锁。 在Java语言规范中,对synchronized关键字进行了优化,包括偏向锁、轻量级锁和重量级锁三种状态,这样可以在不同场景下提高并发性能。 总结来说,synchronized底层原理是通过对象的内部锁来实现线程的互斥访问。通过获取和释放锁的机制,保证了同一时间只有一个线程能够访问被synchronized修饰的代码块,从而保证了线程安全。这种机制虽然简单,但在多线程编程中起着重要的作用。 ### 回答3: synchronizedJava 中用来实现线程同步的关键字。它的底层原理主要是通过对象的监视器锁来实现的。具体来说,Java 中的每个对象都有一个与之相关联的监视器锁,也称为内部锁或互斥锁。 当线程进入一个 synchronized 代码块或方法时,它会尝试获取对象的监视器锁。如果该锁没有被其他线程占用,那么当前线程就可以获取到锁,并进入临界区。如果该锁已经被其他线程占用,则当前线程就会被阻塞,并且进入等待队列。 一旦当前线程进入临界区,它就可以执行 synchronized 代码块或方法中的内容。其他线程如果想要执行该 synchronized 代码块或方法,就必须等待当前线程释放锁。只有当当前线程执行完 synchronized 代码块或方法,且释放了锁,其他线程才有机会获取到锁并执行相应的代码。 synchronized 的原理可以用实例来解释。假设有一个共享资源,例如一个变量,多个线程同时修改该变量的值。如果没有同步机制,可能会导致不可预期的结果。但是当我们使用 synchronized 关键字对修改该变量的代码进行同步,每次只有一个线程能够获取到锁并修改变量的值,这样就保证了线程安全。 总结来说,synchronized 底层原理是通过对象的监视器锁来实现线程之间的同步。它确保了同一时刻只有一个线程能够获取到锁,并且其他线程需要等待锁的释放才能继续执行。这样可以有效地保护共享资源,避免多个线程同时对共享资源进行修改导致的数据不一致性和不可预测性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

激流丶

感觉小弟写的不错,给点鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值