Java中的偏斜锁,轻量级锁,重量级锁的简单理解

文章详细介绍了Java并发控制中的三种锁机制:偏斜锁、轻量级锁和重量级锁。偏斜锁通过记录线程ID减少无竞争情况下的同步开销;轻量级锁利用CAS操作避免多个线程同时访问同一内存地址;而重量级锁涉及用户态和内核态切换,效率较低,通常在竞争激烈时使用。文中通过示例代码展示了这三种锁的工作原理和状态转换过程。
摘要由CSDN通过智能技术生成

偏斜锁

功能

1.消除无竞争情况下的同步原语.

2.提高执行性能.

实现方式

在对象头中记录线程 ID,首次进入锁代码时,直接将对象头中的线程 ID 设置为当前线程,之后只需要进行 CAS 操作即可完成加锁和解锁操作,避免了无竞争情况下的互斥同步操作.

具体实现

当线程进入同步块时,如果该同步对象未被锁住,则认为该线程已经获得了锁,将对象头 Mark Word 拷贝到线程栈帧的锁记录中,并将对象头中的 Mark Word 修改为可偏向状态。之后线程执行完毕后,依然会检查是否存在竞争,如果没有竞争,就将锁恢复回偏向状态。当对象处于偏向状态时,其他线程需要获取该对象的锁时,只需要判断对象头的 Mark Word 是否是可偏向状态,以及其记录的线程 ID 是否和当前线程 ID 是否相等即可.

示例代码

下面代码中,首先创建了一个 ReentrantLock 对象。由于 ReentrantLock 默认开启偏向锁,因此第一个线程加锁时会启用偏向锁。之后第二个线程也加锁,同时会启用偏向锁撤销机制,即偏向锁的状态会变为可偏向状态。当第一个线程解锁时,系统会启动偏向锁撤销机制,并将锁的状态变为轻量级锁。最后两个线程都解锁,此时偏向锁最终被撤销。

public class BiasedLockExample {
   public static void main(String[] args) {
      Lock lock = new ReentrantLock();
      // 偏向锁默认是开启的,可以通过以下参数来关闭偏向锁
      // -XX:-UseBiasedLocking
      // 设置延迟时间,让 JVM 充分运行,获取到尽量多的锁
      // -XX:BiasedLockingStartupDelay=0
      lock.lock(); // 第一个线程加锁(偏向锁)
      System.out.println("Locked by thread1");
      printLockInfo(lock);

      lock.lock(); // 第二个线程加锁,同时启动了偏向锁撤销
      System.out.println("Locked by thread2");
      printLockInfo(lock);

      lock.unlock(); // 第一个线程解锁,启动偏向锁撤销
      System.out.println("Unlocked by thread1");
      printLockInfo(lock);

      lock.unlock(); // 第二个线程解锁,偏向锁最终被撤销
      System.out.println("Unlocked by thread2");
      printLockInfo(lock);
   }

   private static void printLockInfo(Lock lock) {
      ReentrantLock reentrantLock = (ReentrantLock) lock;
      // ReentrantLock 会在对象头记录锁状态信息(包括偏向锁、轻量级锁、重量级锁等)
      // 可以通过 ReentrantLock 的 getState() 方法获取当前锁状态
      System.out.println("Lock state: " + reentrantLock.getState());
      // 可以通过 ReentrantLock 的 isFair() 方法判断当前锁是否是公平锁
      System.out.println("Is fair: " + reentrantLock.isFair());
   }
}

轻量级锁

功能

避免多个线程同时访问同一内存地址.

实现方式

让线程通过 CAS 操作将对象头中的 Mark Word 修改成锁记录指针,如果此时有竞争出现(即多个线程访问同一个内存地址),则当前线程通过自旋等待其他线程释放锁.

具体实现

当第一个线程进入同步块时,虚拟机会使用 CAS 操作将对象头中的 Mark Word 替换为其所在线程栈中的锁记录指针。如果替换成功证明当前线程获取到了锁,并且可以继续执行同步块中的代码。如果替换失败,则表示存在竞争,虚拟机会在同步状态处于轻量级锁状态下自旋等待其他线程释放锁,如果自旋等待结束后仍未获取到锁,则锁会升级至重量级锁。

示例代码

下面代码中,首先创建了一个 ReentrantLock 对象。之后创建了两个线程 A 和 B,分别尝试获取锁。由于没有其他竞争线程,因此两个线程都可以成功获取锁,并且启用轻量级锁机制。当两个线程都成功获取到锁之后,分别解锁并输出锁状态信息。


public class LightweightLockExample {
   public static void main(String[] args) {
      Lock lock = new ReentrantLock();
      new Thread(() -> {
         lock.lock(); // 线程A加锁(轻量级锁)
         System.out.println("Locked by thread A");
         printLockInfo(lock);
         lock.unlock(); // 线程A解锁
         System.out.println("Unlocked by thread A");
         printLockInfo(lock);
      }).start();

      new Thread(() -> {
         lock.lock(); // 线程B加锁(轻量级锁)
         System.out.println("Locked by thread B");
         printLockInfo(lock);
         lock.unlock(); // 线程B解锁
         System.out.println("Unlocked by thread B");
         printLockInfo(lock);
      }).start();
   }

   private static void printLockInfo(Lock lock) {
      ReentrantLock reentrantLock = (ReentrantLock) lock;
      System.out.println("Lock state: " + reentrantLock.getState());
      System.out.println("Is fair: " + reentrantLock.isFair());
   }
}

重量级锁

功能

确保并发执行时同步操作的正确性

实现方式

以对象 Monitor 为基础实现锁;在同步块开始时,虚拟机会创建 Monitor 锁对象,然后将锁对象与被同步对象关联起来;当线程进入同步块时,它尝试获取 Monitor 锁对象的所有权;当线程执行完毕后,会释放 Monitor 锁对象的所有权。由于重量级锁涉及到用户态和内核态的切换,因此效率比偏斜锁和轻量级锁要低,一般只在无法优化的情况下使用

具体实现

当线程进入同步块时,如果没有竞争,则虚拟机会创建表示 Monitor 的 C++ 对象,并将其与对应的 Java 对象进行关联,同时将锁记录中的 owner 指向该 Monitor 对象。当有多个线程竞争该锁时,会使该锁升级为重量级锁。此时虚拟机会将该 Monitor 对象的 owner 指向 null,并记录当前竞争锁的所有线程;之后等待其他线程释放锁,并通过内核 Mutex 机制来阻塞当前线程,直到该线程被唤醒并成功获取到锁。

示例代码

下面代码中,首先创建了一个 ReentrantLock 对象。之后创建了两个线程 A 和 B,分别尝试获取锁。由于线程 A 的执行时间较长,因此当线程 B 尝试获取锁时,会因为发现存在竞争而升级为重量级锁。当线程 A 执行完毕后,会释放锁并降级为轻量级锁。最终,两个线程都可以成功获取锁并解锁,并输出锁状态信息.

public class HeavyweightLockExample {
   public static void main(String[] args) {
      Lock lock = new ReentrantLock();
      new Thread(() -> {
         lock.lock(); // 线程A加锁(轻量级锁)
         System.out.println("Locked by thread A");
         printLockInfo(lock);
         try {
            Thread.sleep(5000L); // 线程A等待5秒钟
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         lock.unlock(); // 线程A解锁
         System.out.println("Unlocked by thread A");
         printLockInfo(lock);
      }).start();

      new Thread(() -> {
         lock.lock(); // 线程B加锁(重量级锁)
         System.out.println("Locked by thread B");
         printLockInfo(lock);
         lock.unlock(); // 线程B解锁
         System.out.println("Unlocked by thread B");
         printLockInfo(lock);
      }).start();
   }

   private static void printLockInfo(Lock lock) {
      ReentrantLock reentrantLock = (ReentrantLock) lock;
      System.out.println("Lock state: " + reentrantLock.getState());
      System.out.println("Is fair: " + reentrantLock.isFair());
   }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

铜镜映无邪Rookie

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值