Java中各类锁的类型、核心作用、适用场景示例代码详解
一、基础锁:内置锁(synchronized)
核心特点
synchronized 是Java内置的隐式锁(JVM层面实现),具备可重入性、默认非公平性,锁会自动释放(方法执行完毕/抛出异常时),无需手动管理。
作用
保证多线程环境下的原子性(操作不可分割)、可见性(一个线程修改的变量对其他线程可见)、有序性(禁止指令重排),解决线程安全问题。
使用场景
适用于简单的线程安全场景(如单例、共享变量修改),开发成本低,无需手动释放锁,适合新手或快速实现同步的场景。
示例代码(三种使用方式)
public class SynchronizedLockDemo {
// 1. 修饰实例方法(锁对象是当前类的实例)
public synchronized void instanceMethodLock() {
System.out.println("实例方法锁:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟业务操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 2. 修饰静态方法(锁对象是当前类的Class对象)
public static synchronized void staticMethodLock() {
System.out.println("静态方法锁:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 3. 修饰代码块(手动指定锁对象,灵活性更高)
public void codeBlockLock() {
// 锁对象可以是任意非null对象,常用this(实例锁)或类对象(全局锁)
synchronized (this) {
System.out.println("代码块锁(实例):" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
SynchronizedLockDemo demo = new SynchronizedLockDemo();
// 测试实例方法锁(多线程竞争同一个实例锁)
new Thread(demo::instanceMethodLock, "线程1").start();
new Thread(demo::instanceMethodLock, "线程2").start();
// 测试静态方法锁(多线程竞争类锁)
new Thread(SynchronizedLockDemo::staticMethodLock, "线程3").start();
new Thread(SynchronizedLockDemo::staticMethodLock, "线程4").start();
// 测试代码块锁
new Thread(demo::codeBlockLock, "线程5").start();
new Thread(demo::codeBlockLock, "线程6").start();
}
}代码解释
- 实例方法锁:锁对象是
demo实例,线程1和线程2会串行执行; - 静态方法锁:锁对象是
SynchronizedLockDemo.class,线程3和线程4串行执行; - 代码块锁:手动指定锁对象,可灵活控制锁的粒度(比如只锁关键代码,而非整个方法)。
二、显式锁:ReentrantLock(可重入锁)
核心特点
实现 java.util.concurrent.locks.Lock 接口,是显式锁(手动加锁/释放锁),支持可重入性,可配置公平锁/非公平锁,还支持中断、超时获取锁、尝试获取锁等高级特性。
作用
比 synchronized 更灵活,解决 synchronized 无法中断、无法超时、无法尝试获取锁的问题,适合复杂的同步场景。
使用场景
- 需要公平锁(按线程等待顺序获取锁)的场景;
- 需要超时等待锁(避免线程永久阻塞)的场景;
- 需要可中断的锁获取(比如优雅停止线程)的场景;
- 需要尝试获取锁(获取不到就执行其他逻辑)的场景。
示例代码(核心特性演示)
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
// 1. 创建ReentrantLock:参数true为公平锁,false(默认)为非公平锁
private static final ReentrantLock FAIR_LOCK = new ReentrantLock(true);
private static final ReentrantLock NON_FAIR_LOCK = new ReentrantLock(false);
// 演示公平锁(按等待顺序获取锁)
public static void fairLockDemo() {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
FAIR_LOCK.lock(); // 手动加锁
try {
System.out.println("公平锁获取成功:" + Thread.currentThread().getName());
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
FAIR_LOCK.unlock(); // 必须在finally中释放锁,避免死锁
}
}, "公平锁线程-" + i).start();
}
}
// 演示尝试获取锁(非阻塞)
public static void tryLockDemo() {
Thread thread1 = new Thread(() -> {
NON_FAIR_LOCK.lock();
try {
System.out.println("线程1持有锁,执行10秒");
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
NON_FAIR_LOCK.unlock();
}
});
Thread thread2 = new Thread(() -> {
boolean isLockAcquired = NON_FAIR_LOCK.tryLock(); // 尝试获取锁,立即返回结果
if (isLockAcquired) {
try {
System.out.println("线程2获取锁成功");
} finally {
NON_FAIR_LOCK.unlock();
}
} else {
System.out.println("线程2获取锁失败,执行备用逻辑");
}
});
thread1.start();
Thread.sleep(1000); // 确保线程1先获取锁
thread2.start();
}
// 演示超时获取锁(避免永久阻塞)
public static void tryLockWithTimeoutDemo() throws InterruptedException {
Thread thread1 = new Thread(() -> {
NON_FAIR_LOCK.lock();
try {
System.out.println("线程1持有锁,执行3秒");
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
NON_FAIR_LOCK.unlock();
}
});
Thread thread2 = new Thread(() -> {
try {
// 尝试获取锁,最多等待5秒,超时返回false
boolean isLockAcquired = NON_FAIR_LOCK.tryLock(5, java.util.concurrent.TimeUnit.SECONDS);
if (isLockAcquired) {
try {
System.out.println("线程2超时等待后获取锁成功");
} finally {
NON_FAIR_LOCK.unlock();
}
} else {
System.out.println("线程2超时等待5秒后仍未获取锁");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("线程2获取锁时被中断");
}
});
thread1.start();
Thread.sleep(100); // 确保线程1先获取锁
thread2.start();
}
public static void main(String[] args) throws InterruptedException {
System.out.println("===== 公平锁演示 =====");
fairLockDemo();
Thread.sleep(3000); // 等待公平锁演示完成
System.out.println("\n===== 尝试获取锁演示 =====");
tryLockDemo();
Thread.sleep(11000); // 等待尝试获取锁演示完成
System.out.println("\n===== 超时获取锁演示 =====");
tryLockWithTimeoutDemo();
}
}代码解释
- 公平锁:线程按等待顺序获取锁,避免“饥饿”(某个线程一直抢不到锁);
tryLock():非阻塞获取锁,获取不到直接执行备用逻辑;tryLock(time, unit):超时等待锁,超时后放弃,避免线程永久阻塞;- 必须在
finally中释放锁,否则锁不会自动释放,易导致死锁。
三、读写锁:ReentrantReadWriteLock
核心特点
基于 Lock 接口,分为读锁(共享锁) 和写锁(独占锁):
- 读-读共享:多个线程可同时获取读锁;
- 读-写/写-读/写-写互斥:写锁与任何锁互斥,保证写操作的原子性。
作用
优化读多写少的场景(如缓存、配置读取),解决 synchronized/ReentrantLock 独占锁导致读操作串行执行的性能问题。
使用场景
- 缓存系统(如本地缓存、Redis客户端缓存);
- 配置中心(高频读取配置,低频修改配置);
- 数据查询系统(多线程读数据,少线程更新数据)。
示例代码
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 读写锁演示:模拟缓存系统(读多写少)
*/
public class ReadWriteLockDemo {
// 缓存容器
private static final Map<String, String> CACHE = new HashMap<>();
// 读写锁
private static final ReentrantReadWriteLock RW_LOCK = new ReentrantReadWriteLock();
// 读锁
private static final ReentrantReadWriteLock.ReadLock READ_LOCK = RW_LOCK.readLock();
// 写锁
private static final ReentrantReadWriteLock.WriteLock WRITE_LOCK = RW_LOCK.writeLock();
// 从缓存读取数据(读操作,加读锁)
public static String get(String key) {
READ_LOCK.lock(); // 加读锁
try {
System.out.println(Thread.currentThread().getName() + " 读取缓存:" + key);
Thread.sleep(500); // 模拟读操作耗时
return CACHE.get(key);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
READ_LOCK.unlock(); // 释放读锁
}
}
// 向缓存写入数据(写操作,加写锁)
public static void put(String key, String value) {
WRITE_LOCK.lock(); // 加写锁
try {
System.out.println(Thread.currentThread().getName() + " 写入缓存:" + key + "=" + value);
Thread.sleep(1000); // 模拟写操作耗时
CACHE.put(key, value);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
WRITE_LOCK.unlock(); // 释放写锁
}
}
public static void main(String[] args) {
// 1. 模拟多线程读缓存(读锁共享,并行执行)
for (int i = 0; i < 5; i++) {
new Thread(() -> get("user:1001"), "读线程-" + i).start();
}
// 2. 模拟写缓存(写锁独占,串行执行)
new Thread(() -> put("user:1001", "张三"), "写线程-1").start();
new Thread(() -> put("user:1002", "李四"), "写线程-2").start();
// 3. 模拟读-写互斥(读线程等待写线程释放锁)
new Thread(() -> get("user:1002"), "读线程-5").start();
}
}代码解释
- 读线程1-5会并行执行(读锁共享),输出几乎同时出现;
- 写线程1和写线程2串行执行(写锁独占);
- 读线程5会等待写线程2释放锁后才执行(读-写互斥);
- 读写锁的核心价值:读多写少时,读操作并行,大幅提升并发性能。
四、设计思想:乐观锁 vs 悲观锁
这是锁的设计思想,而非具体的锁实现,Java中很多锁基于这两种思想实现。
1. 悲观锁
核心特点
认为每次操作都会有线程竞争,先加锁再操作,确保操作过程中无其他线程干扰。
作用
解决高竞争场景下的线程安全问题,牺牲性能换安全。
使用场景
线程竞争激烈、写操作频繁的场景(如库存扣减、转账)。
示例代码(synchronized实现悲观锁)
public class PessimisticLockDemo {
private int count = 0;
// 悲观锁:先加锁再操作
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " 执行自增,count=" + count);
}
public static void main(String[] args) {
PessimisticLockDemo demo = new PessimisticLockDemo();
for (int i = 0; i < 10; i++) {
new Thread(demo::increment, "线程-" + i).start();
}
}2. 乐观锁
核心特点
认为每次操作无竞争,不加锁,仅在更新时通过CAS(Compare And Swap) 检查数据是否被修改:
- 若未修改:更新数据;
- 若已修改:重试或放弃。
作用
无锁竞争时性能极高,牺牲少量安全性(极端场景可能ABA问题)换性能。
使用场景
线程竞争小、读多写少的场景(如计数器、库存扣减优化)。
示例代码(AtomicInteger实现CAS乐观锁)
import java.util.concurrent.atomic.AtomicInteger;
public class OptimisticLockDemo {
// AtomicInteger底层通过CAS实现乐观锁
private static final AtomicInteger COUNT = new AtomicInteger(0);
// 乐观锁:无锁操作,CAS更新
public static void increment() {
// CAS自增:预期值为当前值,更新为当前值+1,失败则重试
int oldValue;
int newValue;
do {
oldValue = COUNT.get(); // 获取当前值
newValue = oldValue + 1; // 计算新值
// CAS尝试更新:如果当前值还是oldValue,就更新为newValue,返回true;否则返回false
} while (!COUNT.compareAndSet(oldValue, newValue));
System.out.println(Thread.currentThread().getName() + " 执行自增,count=" + newValue);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(OptimisticLockDemo::increment, "线程-" + i).start();
}
}
}代码解释
compareAndSet(oldValue, newValue):核心CAS方法,保证原子性;- 循环重试:CAS失败时(其他线程已修改值),重新获取值并再次尝试;
- 无锁竞争时,CAS一次成功,性能远高于悲观锁。
五、进阶锁:StampedLock(Java8新增)
核心特点
读写锁的增强版,支持三种模式:
- 写锁:独占锁,与ReentrantReadWriteLock的写锁一致;
- 悲观读锁:共享锁,与ReentrantReadWriteLock的读锁一致;
- 乐观读锁:无锁模式,仅检查版本号,不加锁,性能比悲观读锁更高。
作用
优化读极多、写极少的场景,乐观读无锁竞争,性能远超ReentrantReadWriteLock。
使用场景
超高并发的读场景(如高频访问的商品详情、用户信息)。
示例代码
import java.util.concurrent.locks.StampedLock;
public class StampedLockDemo {
private double x = 0.0, y = 0.0;
private final StampedLock stampedLock = new StampedLock();
// 写操作:获取写锁
public void move(double deltaX, double deltaY) {
long stamp = stampedLock.writeLock(); // 获取写锁,返回版本戳
try {
x += deltaX;
y += deltaY;
System.out.println("写操作:x=" + x + ", y=" + y);
} finally {
stampedLock.unlockWrite(stamp); // 释放写锁
}
}
// 乐观读操作:无锁,仅检查版本戳
public double distanceFromOrigin() {
long stamp = stampedLock.tryOptimisticRead(); // 获取乐观读戳
double currentX = x, currentY = y; // 读取数据
// 检查乐观读期间是否有写操作(版本戳是否有效)
if (!stampedLock.validate(stamp)) {
// 有写操作,升级为悲观读锁
stamp = stampedLock.readLock();
try {
currentX = x;
currentY = y;
} finally {
stampedLock.unlockRead(stamp); // 释放悲观读锁
}
}
// 计算距离
double distance = Math.sqrt(currentX * currentX + currentY * currentY);
System.out.println("乐观读操作:距离=" + distance);
return distance;
}
public static void main(String[] args) throws InterruptedException {
StampedLockDemo demo = new StampedLockDemo();
// 模拟多线程乐观读
for (int i = 0; i < 5; i++) {
new Thread(demo::distanceFromOrigin, "乐观读线程-" + i).start();
}
// 模拟写操作
Thread.sleep(100);
new Thread(() -> demo.move(1.0, 2.0), "写线程").start();
// 再次模拟乐观读
Thread.sleep(100);
new Thread(demo::distanceFromOrigin, "乐观读线程-5").start();
}
}代码解释
- 乐观读:无锁读取数据,仅通过版本戳验证是否有写操作,性能极高;
- 版本戳失效时:升级为悲观读锁,保证数据一致性;
- 写锁:独占锁,修改数据时阻塞所有读/写操作。
六、其他常见锁
| 锁类型 | 核心特点 | 作用 | 使用场景 |
|---|---|---|---|
| 自旋锁 | 获取锁失败时循环重试,不阻塞线程 | 减少上下文切换,提升性能 | 锁持有时间短、CPU核心多的场景 |
| 分段锁 | 将锁拆分到多个段,仅锁当前段 | 提升并发度 | ConcurrentHashMap(Java7) |
| 偏向锁 | 偏向第一个获取锁的线程,减少开销 | 优化无竞争场景性能 | 单线程访问同步资源的场景 |
| 轻量级锁 | 无竞争时用CAS,竞争时升级为重量级 | 平衡性能与安全 | 低竞争的多线程场景 |
| 重量级锁 | 依赖操作系统互斥量,阻塞线程 | 解决高竞争场景 | 高竞争的多线程场景 |
总结
- 基础场景选synchronized:JVM内置锁,简单易用,自动释放,适合新手或简单同步场景;
- 复杂场景选ReentrantLock:支持公平锁、超时、中断、尝试获取锁,灵活性更高;
- 读多写少选读写锁:ReentrantReadWriteLock(基础)或StampedLock(高性能),读操作并行提升并发;
- 低竞争选乐观锁(CAS):Atomic系列类实现,无锁操作,性能远超悲观锁;高竞争选悲观锁(synchronized/ReentrantLock)。
核心原则:根据竞争程度选择锁——竞争小用乐观锁/轻量级锁,竞争大用悲观锁/重量级锁;读多写少用读写锁,写多读少用独占锁。
到此这篇关于Java中各类锁的类型、核心作用、适用场景示例代码详解的文章就介绍到这了,更多相关java锁的类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
java实现cassandra高级操作之分页实例(有项目具体需求)
这篇文章主要介绍了java实现cassandra高级操作之分页实例(有项目具体需求),具有一定的参考价值,感兴趣的小伙伴们可以参考一下。2017-04-04


最新评论