Java锁的分类与解析
在多线程编程中,锁是确保共享资源不会同时被多个线程访问的关键工具。Java 提供了多种锁的实现方式,可以根据不同的需求选择适合的锁。本文将从多个维度对 Java 的锁进行分类,并对每种锁的特性进行详细解析。
锁的分类
锁的分类并没有唯一标准,而是取决于观察的角度或划分的维度。从不同维度出发,可以对锁进行多种类型的划分。以下是基于常见特性(如互斥性、重入性、公平性等)的一些主流分类方式,供参考。
1. 锁的基本对比
锁名(例子) | 重入性 | 互斥性 | 公平性 | 分布式性 | 实现方式 | 适用场景 |
---|---|---|---|---|---|---|
synchronized | 可重入 | 独占 | 非公平(不可配置) | 否 | 隐式锁 | 基础同步,语法简单,低并发场景 |
ReentrantLock | 可重入 | 独占 | 公平 / 非公平(默认非公平) | 否 | 显式锁 | 替代 synchronized,控制更灵活 |
ReadWriteLock | 可重入 | 写独占 / 读共享 | 公平 / 非公平(默认非公平) | 否 | 显式锁 | 读多写少,读写分离 |
StampedLock | 不可重入 | 写独占 / 读共享 | 非公平(不可配置) | 否 | 显式锁 | 高性能读多写少,需谨慎使用不可重入 |
Redis 分布式锁 | 不支持(除非设置标志) | 独占 | 非公平(不可配置) | 是 | 显式锁 | 分布式场景下的互斥控制(使用 Redisson 等库) |
Zookeeper 分布式锁 | 不支持(除非设置标志) | 独占 | 非公平(不可配置) | 是 | 显式锁 | 分布式场景下的互斥控制(使用 Curator 等库) |
2. 锁的类型与特性分析
2.1 重入性(同一线程)
- 可重入锁:同一线程如果已经持有锁,【再次请求该锁不会】导致阻塞或死锁。例如,
ReentrantLock
支持重入,即线程可以在持有锁的情况下再次请求锁。 - 不可重入锁:同一线程如果已经持有锁,【再次请求该锁会】导致线程被阻塞,直到该锁被释放。
StampedLock
就是不支持重入的锁。
2.2 互斥性(多线程)
- 独占锁:同一时刻【只有一个线程】可以获取锁,其他线程必须等待锁被释放才能获得锁。这种锁适用于需要保证资源不被并发访问的场景。
- 共享锁:同一时刻【多个线程】可以获取锁,通常适用于【读操作】。例如,
ReadWriteLock
提供了共享锁,允许多个线程同时读取共享数据,但写操作是独占的。
2.3 公平性(多线程)
- 公平锁:按照线程请求的【时间先后顺序分配锁】,避免某些线程长时间得不到锁,避免饥饿现象。例如,
ReentrantLock
可以配置为公平锁。 - 非公平锁:锁释放后,所有线程尝试【竞争锁】,谁先获得锁由线程调度决定,【不按请求时间先后顺序分配】。这可能导致部分线程长时间等待。
synchronized
与默认的ReentrantLock
属于非公平锁。
2.4 分布式性(多进程)
- 分布式锁:分布式锁是用于【多个进程】之间实现互斥访问的锁,确保在分布式系统中不同进程对共享资源的访问是互斥的。分布式锁通常依赖外部的分布式协调系统来实现,如 Zookeeper、Redis、etcd 等。
- 非分布式锁:非分布式锁是只在【单个进程】内使用的锁,主要用于控制同一进程中的多个线程之间的访问互斥。它无法跨进程或跨节点进行资源同步,限制了它的适用范围。
2.5 实现方式(锁管理主体)
- 隐式锁:锁的管理由【系统(如 JVM)】自动完成控制锁的获取和释放。
synchronized
是最典型的隐式锁,开发者仅通过简单的代码块来控制同步。 - 显式锁:锁的管理由开发者通过明确的代码控制,通常是通过实现
Lock
接口的类来控制锁的获取与释放,【开发者】必须显式地控制锁的获取与释放。ReentrantLock
和ReadWriteLock
是典型的显式锁。
3. 锁的应用场景
- 基础同步:
synchronized
适用于简单的同步需求,语法简洁,但在高并发场景下可能不够灵活。 - 灵活控制:
ReentrantLock
提供了更高的灵活性,可以指定锁的公平性,支持条件变量等功能。 - 读多写少:
ReadWriteLock
适用于读多写少的场景,可以提升读操作的并发度。 - 高性能场景:
StampedLock
适用于对性能要求较高的场景,尤其是在读多写少的环境下。 - 分布式锁:
Redis
和Zookeeper
分布式锁适用于分布式环境中的互斥控制,确保分布式系统中的数据一致性。
总结
Java 提供了多种类型的锁来满足不同场景下的并发需求。从 synchronized
到 ReentrantLock
,再到分布式锁如 Redis
和 Zookeeper
,每种锁都有其适用的场景和优势。在选择锁的实现方式时,开发者需要根据实际的业务需求、并发量、性能要求等因素做出合理的选择。