Java 锁的七大分类与应用解析:从偏向锁到可中断锁

在并发编程中,锁是线程同步的关键。通过锁的机制,多个线程可以协调访问共享资源,保证数据的一致性。根据不同的需求,Java 提供了多种锁机制,每种锁的特点和适用场景各异。本文将深入分析 Java 锁的七大分类及其特点,帮助你全面理解锁的使用,并在项目中更好地应用。

1. 偏向锁/轻量级锁/重量级锁

这三种锁特指 synchronized 锁的状态,它们通过 JVM 内部的对象头(Mark Word)来控制锁的状态。这些锁的主要作用是根据线程竞争的程度来动态调整锁的类型,确保线程的执行效率。

1.1 偏向锁

  • 定义:偏向锁是最轻量级的锁,适用于线程没有竞争的场景。当某个线程获得偏向锁后,其他线程无法获得该锁,直到偏向锁被释放。
  • 特点:性能最优,不会进行加锁操作,仅仅在第一次获取时进行标记。
  • 适用场景:适合线程访问共享资源时没有竞争的场景。

1.2 轻量级锁

  • 定义:当偏向锁存在竞争时,它会升级为轻量级锁。轻量级锁通过 CAS 操作来避免线程阻塞。
  • 特点:通过自旋(CAS)尝试获取锁,避免线程阻塞,性能较高。
  • 适用场景:适合多线程之间竞争较少的场景。

1.3 重量级锁

  • 定义:当轻量级锁无法满足需求时,锁会升级为重量级锁。重量级锁通过操作系统的同步机制来实现,线程获取不到锁时会被阻塞。
  • 特点:性能较差,因为涉及到线程的上下文切换和阻塞。
  • 适用场景:适合竞争激烈且锁持有时间较长的场景。

锁的升级路径:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

2. 可重入锁/非可重入锁

锁的可重入性是指线程在持有锁的情况下,是否能够再次获取相同的锁。

2.1 可重入锁

  • 定义:可重入锁允许同一个线程多次获得同一把锁。
  • 典型实现ReentrantLock 就是一个典型的可重入锁,它允许线程在持有锁的情况下再次获得该锁。
  • 适用场景:适合在递归调用或者需要多次获取同一锁的场景。

2.2 非可重入锁

  • 定义:非可重入锁要求线程在释放锁后才能再次获得锁。
  • 典型实现Mutex 锁通常是非可重入锁。
  • 适用场景:适合对同一资源的访问需要严格控制的场景。

3. 共享锁/独占锁

锁的共享性决定了它是否允许多个线程同时获得锁。

3.1 共享锁

  • 定义:共享锁允许多个线程同时获得锁。
  • 典型实现:读写锁中的读锁是共享锁,可以多个线程同时获取并读取数据。
  • 适用场景:适合读操作较多的场景,减少线程等待时间。

3.2 独占锁

  • 定义:独占锁保证在同一时刻,只有一个线程可以获得锁。
  • 典型实现:写锁是独占锁,只有一个线程能够写入数据。
  • 适用场景:适合对数据进行修改时使用。

4. 公平锁/非公平锁

公平性锁是指线程能否按照请求的顺序公平地获得锁。

4.1 公平锁

  • 定义:公平锁保证线程按照申请锁的顺序依次获得锁,类似于“先来先得”。
  • 典型实现ReentrantLock 在构造时指定 true 可实现公平锁。
  • 适用场景:适合对线程公平性要求较高的场景。

4.2 非公平锁

  • 定义:非公平锁不保证线程获取锁的顺序,可能会出现线程插队现象。
  • 典型实现ReentrantLock 默认实现为非公平锁。
  • 适用场景:适合对公平性要求不高且希望提高性能的场景。

5. 悲观锁/乐观锁

悲观锁和乐观锁的区别在于对资源争用的假设:悲观锁认为资源会发生冲突,而乐观锁认为资源不会发生冲突。

5.1 悲观锁

  • 定义:悲观锁假设线程间会发生资源争用,在访问共享资源前先加锁。
  • 典型实现synchronizedReentrantLock 是悲观锁的常见实现。
  • 适用场景:适合多线程写操作较多、临界区代码复杂的场景。

5.2 乐观锁

  • 定义:乐观锁假设线程间不会发生冲突,不加锁,访问数据时判断是否发生了冲突,若冲突则重试。
  • 典型实现:CAS(Compare-And-Swap)是乐观锁的实现方式,AtomicInteger 类就是一个典型的乐观锁实现。
  • 适用场景:适合读操作多、写操作少的场景。

6. 自旋锁/非自旋锁

自旋锁和非自旋锁的区别在于获取锁失败时是否立即放弃。

6.1 自旋锁

  • 定义:自旋锁是一种忙等待的锁,当线程无法获取锁时,线程会在原地等待一段时间,然后继续尝试获取锁。
  • 典型实现ReentrantLocktryLock 方法就是一种自旋锁的实现。
  • 适用场景:适合锁竞争较少、线程等待时间短的场景。

6.2 非自旋锁

  • 定义:非自旋锁是在无法获取锁时,线程直接放弃或进入阻塞状态,等待唤醒。
  • 典型实现synchronized 锁就是一种非自旋锁。
  • 适用场景:适合锁竞争较激烈的场景。

7. 可中断锁/不可中断锁

可中断锁是指线程在获取锁的过程中可以响应中断,而不可中断锁则在获取锁时无法中断。

7.1 可中断锁

  • 定义:可中断锁在获取锁时可以响应中断。
  • 典型实现ReentrantLock 提供了 lockInterruptibly 方法,允许线程在等待获取锁时响应中断。
  • 适用场景:适合需要中断机制的任务,避免线程长时间阻塞。

7.2 不可中断锁

  • 定义:不可中断锁一旦开始获取,就无法响应中断,直到获取锁成功或出现异常。
  • 典型实现synchronized 锁是不可中断的锁。
  • 适用场景:适合不要求中断的任务,保证锁的稳定性。

总结

在多线程编程中,锁的选择对程序的性能和稳定性至关重要。根据不同的应用场景和需求,选择合适的锁类型,可以有效提高并发程序的效率。本文介绍了 Java 锁的七大分类,包括偏向锁、轻量级锁、可重入锁、共享锁、悲观锁、乐观锁、自旋锁和可中断锁,了解它们的特点和应用场景,能够帮助你更好地设计并发系统,避免常见的性能瓶颈和同步问题。

🌟 关注我的CSDN博客,收获更多技术干货! 🌟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dm菜鸟编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值