Java中锁分类及在什么场景下使用
一、基础分类(按实现方式)
这是最核心的分类维度,直接决定锁的使用方式和核心能力。
1. 内置锁(synchronized)- 隐式锁
核心定义
Java 关键字,JVM 层面实现的隐式锁(无需手动释放),是最基础、使用最广泛的锁。JDK1.6 后引入「锁升级」机制,性能大幅提升。
核心特点
- 可重入、默认非公平锁;
- 自动加锁 / 解锁(方法 / 代码块执行完自动释放,无需手动处理);
- 底层依赖对象头的
Mark Word+ 监视器锁(ObjectMonitor); - 支持锁升级(偏向锁→轻量级锁→重量级锁),适配不同并发场景。
适用场景
- 简单互斥场景(如方法 / 代码块的线程安全);
- 并发度不高、代码简洁性优先的场景;
- 不需要灵活特性(如可中断、超时获取锁)的场景;
- 绝大多数普通业务场景(JVM 优化后性能接近显式锁)。
代码示例
public class SynchronizedDemo {
// 1. 实例方法锁(对象锁):锁当前实例对象
public synchronized void objectLock() {
System.out.println(Thread.currentThread().getName() + "获取对象锁");
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
// 2. 静态方法锁(类锁):锁当前类的Class对象
public static synchronized void classLock() {
System.out.println(Thread.currentThread().getName() + "获取类锁");
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
// 3. 代码块锁:自定义锁对象(灵活度最高)
private final Object lockObj = new Object();
public void blockLock() {
synchronized (lockObj) {
System.out.println(Thread.currentThread().getName() + "获取代码块锁");
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
// 竞争同一对象锁,串行执行
new Thread(demo::objectLock, "线程1").start();
new Thread(demo::objectLock, "线程2").start();
}
}2. 显式锁(Lock 接口)- 手动锁
核心定义
JUC 包下java.util.concurrent.locks.Lock接口的实现类,手动加锁 / 释放锁(需在finally中释放,避免死锁),是 synchronized 的补充和增强。
核心实现类 & 特点
| 实现类 | 核心特性 |
|---|---|
| ReentrantLock | 可重入、支持公平 / 非公平、可中断、超时获取锁 |
| ReentrantReadWriteLock | 读写分离(读共享、写独占)、可重入 |
| StampedLock | 支持乐观读、读写锁、写锁,性能优于读写锁 |
适用场景
- 需要灵活锁控制(如可中断、超时获取锁、公平锁)的场景;
- 读多写少的高并发场景(选 ReentrantReadWriteLock/StampedLock);
- 高并发、需要精细控制锁生命周期的场景。
代码示例(ReentrantLock)
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
// 公平锁(按请求顺序获取),默认非公平锁(性能更高)
private static final ReentrantLock lock = new ReentrantLock(true);
public static void doTask() {
// 1. 加锁(可替换为lockInterruptibly():可中断锁)
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取锁");
// 模拟业务操作
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
} finally {
// 2. 释放锁(必须放finally,否则死锁)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放锁");
}
}
}
public static void main(String[] args) {
new Thread(ReentrantLockDemo::doTask, "线程A").start();
new Thread(ReentrantLockDemo::doTask, "线程B").start();
}
}二、进阶分类(按锁的核心特性)
基于锁的行为和并发特性分类,帮你理解锁的底层逻辑和适用场景。
1. 可重入锁 vs 不可重入锁
| 类型 | 定义 | 示例 | 适用场景 |
|---|---|---|---|
| 可重入锁 | 同一线程可多次获取同一把锁,不会死锁 | synchronized、ReentrantLock | 所有业务场景(递归调用、同一线程多次操作共享资源) |
| 不可重入锁 | 同一线程多次获取同一锁会死锁 | 自定义简单自旋锁(未处理重入) | 极少使用(仅严格限制锁获取次数的特殊场景) |
可重入锁示例(synchronized 递归调用)
public class ReentrantDemo {
public synchronized void outer() {
System.out.println("外层方法获取锁");
inner(); // 同一线程再次获取同一锁,无死锁
}
public synchronized void inner() {
System.out.println("内层方法获取锁");
}
public static void main(String[] args) {
new ReentrantDemo().outer(); // 正常执行,无死锁
}
}2. 乐观锁 vs 悲观锁
这是并发设计思想的分类,而非具体锁实现。
| 类型 | 核心思想 | 实现方式 | 适用场景 |
|---|---|---|---|
| 悲观锁 | 假设必有竞争,先锁后执行 | synchronized、ReentrantLock | 高冲突、写多读少(如库存扣减、转账) |
| 乐观锁 | 假设无竞争,先执行后检测 | CAS(Atomic 类)、版本号 | 低冲突、读多写少(如计数器、缓存更新) |
乐观锁示例(CAS 实现)
import java.util.concurrent.atomic.AtomicInteger;
public class OptimisticLockDemo {
// CAS是乐观锁的核心实现
private static final AtomicInteger count = new AtomicInteger(0);
// 原子自增(无锁,冲突时重试)
public static void increment() {
count.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) increment();
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("最终计数:" + count.get()); // 2000(线程安全)
}
}3. 公平锁 vs 非公平锁
| 类型 | 定义 | 示例 | 适用场景 |
|---|---|---|---|
| 公平锁 | 按请求顺序获取锁,先到先得 | ReentrantLock(true) | 对公平性要求高(如任务排队、避免线程饥饿) |
| 非公平锁 | 不按顺序,线程可插队获取锁 | synchronized、ReentrantLock() | 大部分场景(优先性能,容忍轻微饥饿) |
4. 读写锁(ReentrantReadWriteLock)- 共享 + 独占锁
核心定义
将锁拆分为「读锁(共享锁)」和「写锁(独占锁)」,核心规则:
- 读 - 读共享:多个线程可同时获取读锁;
- 读 - 写互斥:读锁和写锁不能同时持有;
- 写 - 写互斥:多个线程不能同时获取写锁。
适用场景
读多写少的场景(如缓存、配置读取、商品详情页、数据查询),相比普通独占锁,能大幅提升读并发效率。
代码示例
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private static final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
private static final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
private static int cacheData = 0; // 模拟缓存数据
// 读操作(共享锁,多线程同时执行)
public static void readCache() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "读取缓存:" + cacheData);
Thread.sleep(200); // 模拟读耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
readLock.unlock();
}
}
// 写操作(独占锁,串行执行)
public static void updateCache(int newData) {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "更新缓存:" + newData);
cacheData = newData;
Thread.sleep(200); // 模拟写耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
writeLock.unlock();
}
}
public static void main(String[] args) {
// 5个读线程(可同时执行,并发效率高)
for (int i = 0; i < 5; i++) {
new Thread(ReadWriteLockDemo::readCache, "读线程" + i).start();
}
// 1个写线程(独占,所有读线程等待)
new Thread(() -> updateCache(100), "写线程").start();
}
}5. synchronized 的锁升级(偏向锁→轻量级锁→重量级锁)
JDK1.6 为优化 synchronized 引入的自适应锁机制,锁级别从低到高升级(不可逆),适配不同并发场景:
| 锁类型 | 核心特点 | 适用场景 |
|---|---|---|
| 偏向锁 | 锁偏向第一个线程,无竞争时零开销 | 单线程执行同步代码(如初始化资源) |
| 轻量级锁 | 多线程交替竞争,CAS 自旋获取锁 | 少量线程(2-3 个)交替执行 |
| 重量级锁 | 多线程激烈竞争,线程阻塞(OS 层面) | 大量线程同时竞争锁 |
6. 自旋锁 vs 阻塞锁
| 类型 | 定义 | 示例 | 适用场景 |
|---|---|---|---|
| 自旋锁 | 获取锁失败时循环重试(自旋),不阻塞 | CAS、synchronized 轻量级锁 | 锁持有时间短、低冲突(如简单变量更新) |
| 阻塞锁 | 获取锁失败时线程阻塞,释放 CPU | synchronized 重量级锁、ReentrantLock | 锁持有时间长、高冲突(如复杂业务逻辑) |
7. 分段锁(ConcurrentHashMap JDK1.7)
核心定义
将数据拆分为多个「段(Segment)」,每个段独立加锁,不同段的操作互不阻塞,并发度 = 段数(默认 16)。
适用场景
JDK1.7 的 ConcurrentHashMap(JDK1.8 后用 CAS+Synchronized 替代),适用于高并发读写 Map 的场景。
三、实战选型指南(核心)
| 业务场景 | 推荐锁类型 | 选型原因 |
|---|---|---|
| 简单互斥、代码简洁 | synchronized | 隐式锁,无需手动释放,JVM 优化优异 |
| 可中断 / 超时 / 公平锁 | ReentrantLock | 支持灵活的锁控制特性 |
| 读多写少(缓存 / 查询) | ReentrantReadWriteLock/StampedLock | 读共享,大幅提升读并发效率 |
| 低冲突、简单变量更新 | CAS(AtomicInteger/AtomicLong) | 无锁,性能最高,避免自旋消耗 |
| 高冲突、写多读少(转账) | synchronized/ReentrantLock | 悲观锁,避免高冲突下的 CAS 自旋 CPU 开销 |
| 公平性要求高(任务排队) | ReentrantLock(true) | 按请求顺序获取锁,避免线程饥饿 |
| 高并发 Map 操作 | ConcurrentHashMap | JDK1.8 用 CAS+Synchronized,兼顾性能和安全性 |
总结
关键点回顾
- 基础核心:synchronized(简单、通用)和 Lock 接口(灵活、高级)是 Java 锁的两大基石,前者适用于普通场景,后者适用于需要灵活控制的场景;
- 特性选型:读多写少选读写锁,低冲突选乐观锁 / CAS,高冲突选悲观锁,公平性要求高选公平锁;
- 性能原则:优先选择「开销低」的锁(如偏向锁、CAS),高冲突场景才用「高开销」的阻塞锁(如重量级锁);
- 实战建议:90% 的普通业务场景用 synchronized 即可,仅在需要可中断、超时、读写分离时才用 Lock 接口实现类。
到此这篇关于Java中锁分类及在什么场景下使用的文章就介绍到这了,更多相关java锁使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
URLConnection发送HTTP请求的方法_动力节点Java学院整理
这篇文章主要介绍了URLConnection发送HTTP请求的方法,主要介绍了如何通过Java(模拟浏览器)发送HTTP请求,有兴趣的可以了解一下2017-07-07
SpringCloudConfig之client端报错Could not resolve placeholder问
这篇文章主要介绍了SpringCloudConfig之client端报错Could not resolve placeholder ‘from‘ in value “${from}“问题及解决方案,具有很好的参考价值,希望对大家有所帮助2022-12-12


最新评论