欢迎浏览高耳机的博客
希望我们彼此都有更好的收获
感谢三连支持!
在多线程编程中,锁是保证数据一致性和线程安全的重要机制.本文将直观且简洁的介绍常见的锁策略,包括它们的基本逻辑,使用场景以及优缺点.
悲观锁 与 乐观锁
悲观锁:预防性策略
悲观锁是一种主动锁定策略,它假设最坏的情况,即冲突总是会发生。在数据被访问时,悲观锁会直接将数据锁定,以防止其他线程的修改。这种策略适用于写操作频繁的场景,可以通过数据库的排它锁或代码中的synchronized
关键字实现
特点:
- 适用于写操作多的场景。
- 可以防止脏读和不可重复读。
乐观锁:事后检查
与悲观锁相反,乐观锁是一种被动锁定策略,它假设冲突很少发生。在数据被访问时,乐观锁不会直接锁定数据,而是在数据提交时检查是否发生了冲突。它允许多个线程同时访问数据,如果检测到冲突,乐观锁会回滚并重试操作。这种策略适用于读操作多的场景
特点:
- 适用于读操作多的场景。
- 通过版本号或CAS(Compare-And-Swap)操作实现。
实现方式:
- 通常使用数据库的乐观锁机制或在代码中使用
Atomic
类。
重量级锁 与 轻量级锁: 性能考量
重量级锁
重量级锁是指操作系统级别的锁,它涉及到用户态和内核态的切换,因此开销较大
特点:
- 适用于需要严格同步的场景。
- 实现了操作系统级别的同步。
轻量级锁
轻量级锁是指在用户态实现的锁,它避免了用户态和内核态之间的切换,因此开销较小。
特点:
- 适用于锁竞争激烈的场景。
- 实现了用户态的同步。
重量级锁和轻量级锁的区别在于操作系统的介入程度。重量级锁涉及到用户态和内核态的切换,因此开销较大,适用于锁竞争激烈的场景。而轻量级锁在用户态实现,避免了切换开销,适用于锁竞争不激烈或锁持有时间短的场景
挂起等待锁 与 自旋锁 : 阻塞VS忙等待
挂起等待锁
挂起等待锁是一种当锁不可用时,将线程挂起等待的锁策略。这种策略可以减少CPU的占用,但在锁竞争激烈时可能会导致线程调度开销。
特点:
- 当锁不可用时,线程会被挂起。
- 适用于锁持有时间短的场景。
实现方式:
- 通常通过操作系统的线程挂起和唤醒机制实现。
自旋锁
自旋锁是一种忙等待策略,当锁不可用时,线程不断循环检查锁状态的锁策略。这种策略可以减少线程调度的开销,但在锁持有时间长时可能会导致CPU资源浪费。
特点:
- 当锁不可用时,线程会不断自旋检查。
- 适用于锁持有时间短且CPU资源充足的场景。
实现方式:
- 通常通过循环检查锁状态的代码实现。
公平锁 与 非公平锁: 锁的分配策略
公平锁
与我们生活中所谓的"公平"不同,公平锁是指按照线程请求锁的顺序来分配锁的策略,以"先来后到"作为基准,而不是为每个线程平均分配锁。这种策略可以保证线程的公平性,但可能会降低系统的吞吐量。
特点:
- 保证了线程请求锁的顺序。
- 适用于需要保证公平性的场景。
实现方式:
- 通常通过队列来管理线程的请求顺序。
非公平锁
非公平锁是指不保证线程请求锁的顺序,哪个线程先抢到锁就分配给哪个线程的策略。这种策略可以提高系统的吞吐量,但可能会导致线程饥饿。
特点:
- 不保证线程请求锁的顺序。
- 适用于对吞吐量要求高的场景。
实现方式:
- 通常直接尝试获取锁,不进行排队。
公平锁确保线程获取锁的顺序与其请求顺序一致,而非公平锁则不保证这一点。公平锁可以减少线程饥饿,但可能会降低系统的吞吐量。非公平锁可能会提高吞吐量,但增加线程饥饿的风险
可重入锁 与 不可重入锁: 锁的重入性
可重入锁
可重入锁是指一个线程可以多次获取同一把锁的策略。这种策略可以避免死锁,但可能会增加锁的管理复杂度。
特点:
- 同一线程可以多次获取同一把锁。
- 适用于递归调用或多层嵌套的场景。
实现方式:
- 通常通过维护一个计数器来记录同一个线程获取锁的次数。
不可重入锁
不可重入锁是指一个线程不能多次获取同一把锁的策略。这种策略可以减少锁的管理复杂度,但可能会导致死锁。
特点:
- 同一线程不能多次获取同一把锁。
- 适用于简单的同步场景。
实现方式:
- 通常直接尝试获取锁,不考虑重入的情况。
读写锁: 平衡读与写
读写锁是一种特殊类型的锁,允许多个读操作同时进行,但写操作需要独占访问的锁策略。这种策略适用于读操作多于写操作的场景,可以提高系统的并发性,但可能会增加锁的管理复杂度。
特点:
- 允许多个读操作同时进行。
- 写操作需要独占访问。
- 适用于读操作多于写操作的场景。
实现方式:
- 通常通过维护读锁和写锁两个锁,分别控制读操作和写操作。
Synchronized优化过程: 锁的优化策略
在Java中,synchronized
关键字有一些优化策略,以减少锁的开销:
锁消除
锁消除是指编译器在编译期间,如果确定一个锁不会被其他线程访问,就消除这个锁的过程。这种策略可以减少锁的开销,提高程序的执行效率。
粒度 与 锁粗化
粒度是指锁的作用范围。细粒度锁可以提高程序的并发性,但会增加锁的管理复杂度。粗粒度锁可以减少锁的管理复杂度,但可能会降低程序的并发性。
锁粗化是指将多个细小的锁操作合并成一个大的锁操作的过程。这种策略可以减少锁的开销,但可能会降低程序的并发性。
总结
在多线程编程的世界中,锁策略的选择对于确保数据一致性、提高系统性能以及优化资源利用至关重要。每种锁策略都有其独特的应用场景和性能考量。随着我们对这些锁策略的深入理解,我们可以更加精准地选择或设计适合特定应用需求的同步机制。这些策略都是我们在构建高效、稳定系统时的重要工具。
希望这篇博客能为你理解常见的锁策略提供一些帮助。
如有不足之处请多多指出。
我是高耳机。