本文目录导读:

我来为你展示几个可重入锁的经典案例,从基础到进阶逐步深入。
什么是可重入锁?
可重入锁(Reentrant Lock)指的是:同一个线程可以多次获取同一个锁而不会发生死锁。
public class ReentrantDemo {
public synchronized void methodA() {
System.out.println("进入方法A");
// 同一个线程可以再次获取锁
methodB(); // 不会死锁,因为synchronized是可重入的
}
public synchronized void methodB() {
System.out.println("进入方法B");
}
public static void main(String[] args) {
ReentrantDemo demo = new ReentrantDemo();
demo.methodA(); // 输出:进入方法A → 进入方法B
}
}
案例一:递归调用(最典型场景)
public class RecursiveLockDemo {
private int count = 0;
// 使用synchronized实现可重入
public synchronized void increment() {
count++;
System.out.println("当前值: " + count + ", 线程: " + Thread.currentThread().getName());
if (count < 5) {
increment(); // 递归调用,同一个线程再次进入
}
}
// 使用ReentrantLock实现可重入
private ReentrantLock lock = new ReentrantLock();
public void incrementWithLock() {
lock.lock();
try {
count++;
System.out.println("Lock值: " + count + ", 持有锁次数: " + lock.getHoldCount());
if (count < 3) {
incrementWithLock(); // 递归调用
}
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
RecursiveLockDemo demo = new RecursiveLockDemo();
demo.increment(); // 输出1-5,不会死锁
}
}
案例二:继承关系中的重入
public class InheritanceReentrantDemo {
static class Parent {
public synchronized void doSomething() {
System.out.println("父类方法执行 - " + Thread.currentThread().getName());
}
}
static class Child extends Parent {
@Override
public synchronized void doSomething() {
System.out.println("子类方法执行 - " + Thread.currentThread().getName());
// 调用父类方法,需要再次获取锁
super.doSomething(); // 这是可重入的,不会死锁
}
}
public static void main(String[] args) {
Child child = new Child();
child.doSomething();
}
}
案例三:ReentrantLock的高级特性
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockAdvancedDemo {
// 公平锁:true表示公平锁
private ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁:默认
private ReentrantLock nonFairLock = new ReentrantLock();
public void demonstrateFeatures() throws InterruptedException {
// 1. 可重入性验证
fairLock.lock();
try {
System.out.println("第一次获取锁");
System.out.println("锁持有次数: " + fairLock.getHoldCount());
fairLock.lock(); // 再次获取
try {
System.out.println("第二次获取锁");
System.out.println("锁持有次数: " + fairLock.getHoldCount());
} finally {
fairLock.unlock();
System.out.println("释放一次,剩余持有次数: " + fairLock.getHoldCount());
}
} finally {
fairLock.unlock();
System.out.println("释放一次,剩余持有次数: " + fairLock.getHoldCount());
}
// 2. 可中断性
Thread waitingThread = new Thread(() -> {
try {
nonFairLock.lockInterruptibly();
try {
System.out.println("线程获取到锁");
} finally {
nonFairLock.unlock();
}
} catch (InterruptedException e) {
System.out.println("线程被中断,无法获取锁");
}
});
waitingThread.start();
Thread.sleep(100);
waitingThread.interrupt(); // 中断等待的线程
// 3. 尝试获取锁(非阻塞)
if (nonFairLock.tryLock()) {
try {
System.out.println("成功获取锁");
} finally {
nonFairLock.unlock();
}
} else {
System.out.println("锁被占用,操作失败");
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockAdvancedDemo demo = new ReentrantLockAdvancedDemo();
demo.demonstrateFeatures();
}
}
案例四:数据库连接池(实际应用)
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class ConnectionPoolDemo {
private static class Connection {
private boolean inUse = false;
public void use() {
System.out.println("连接被使用 - " + Thread.currentThread().getName());
// 模拟耗时操作
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private final Connection[] connections;
private final boolean[] used;
private final ReentrantLock lock = new ReentrantLock();
private final java.util.concurrent.locks.Condition available = lock.newCondition();
public ConnectionPoolDemo(int size) {
connections = new Connection[size];
used = new boolean[size];
for (int i = 0; i < size; i++) {
connections[i] = new Connection();
}
}
public Connection getConnection() throws InterruptedException {
lock.lock();
try {
while (true) {
// 查找空闲连接
for (int i = 0; i < connections.length; i++) {
if (!used[i]) {
used[i] = true;
System.out.println("获取连接 " + i + " - " + Thread.currentThread().getName());
return connections[i];
}
}
// 没有空闲连接,等待
System.out.println("等待连接... - " + Thread.currentThread().getName());
available.await(1, TimeUnit.SECONDS);
}
} finally {
lock.unlock();
}
}
public void returnConnection(Connection connection) {
lock.lock();
try {
for (int i = 0; i < connections.length; i++) {
if (connections[i] == connection && used[i]) {
used[i] = false;
System.out.println("归还连接 " + i + " - " + Thread.currentThread().getName());
available.signalAll();
return;
}
}
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ConnectionPoolDemo pool = new ConnectionPoolDemo(2);
// 模拟多个线程使用连接池
for (int i = 0; i < 4; i++) {
new Thread(() -> {
try {
Connection conn = pool.getConnection();
conn.use();
pool.returnConnection(conn);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "线程-" + i).start();
}
}
}
案例五:性能对比和注意事项
public class PerformanceComparisonDemo {
// 循环次数
private static final int LOOPS = 1000000;
// 使用synchronized
private int syncCount = 0;
// 使用ReentrantLock
private int lockCount = 0;
private final ReentrantLock lock = new ReentrantLock();
public synchronized void incrementSync() {
syncCount++;
}
public void incrementLock() {
lock.lock();
try {
lockCount++;
} finally {
lock.unlock();
}
}
public void comparePerformance() {
// 测试synchronized
long startTime = System.nanoTime();
for (int i = 0; i < LOOPS; i++) {
incrementSync();
}
long syncTime = System.nanoTime() - startTime;
// 测试ReentrantLock
long startTime2 = System.nanoTime();
for (int i = 0; i < LOOPS; i++) {
incrementLock();
}
long lockTime = System.nanoTime() - startTime2;
System.out.println("synchronized耗时: " + syncTime / 1000000 + "ms");
System.out.println("ReentrantLock耗时: " + lockTime / 1000000 + "ms");
}
public static void main(String[] args) {
PerformanceComparisonDemo demo = new PerformanceComparisonDemo();
demo.comparePerformance();
// 注意事项示例
System.out.println("\n=== 注意事项 ===");
System.out.println("1. 必须成对使用lock/unlock,用try-finally保证");
System.out.println("2. 不要忘了释放锁,否则会造成死锁");
System.out.println("3. 尽量使用锁的作用域最小化");
System.out.println("4. 考虑使用tryLock避免死锁");
}
}
常见问题解答
public class CommonQuestionsDemo {
// Q1: synchronized和ReentrantLock的区别?
public static void question1() {
System.out.println("=== 区别 ===");
System.out.println("1. synchronized是JVM层面,ReentrantLock是JDK层面");
System.out.println("2. ReentrantLock支持中断、超时、公平锁");
System.out.println("3. ReentrantLock需要手动释放锁");
System.out.println("4. synchronized会自动释放锁");
}
// Q2: 什么情况下会发生死锁?
public static void question2() {
Object lockA = new Object();
Object lockB = new Object();
// 死锁示例
Thread thread1 = new Thread(() -> {
synchronized (lockA) {
System.out.println("线程1持有锁A");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockB) {
System.out.println("线程1持有锁B");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lockB) {
System.out.println("线程2持有锁B");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lockA) {
System.out.println("线程2持有锁A");
}
}
});
System.out.println("=== 这会产生死锁,因为线程互相等待对方持有的锁 ===");
System.out.println("解决方式:\n1. 按顺序获取锁\n2. 使用tryLock设置超时\n3. 使用Lock-free数据结构");
}
public static void main(String[] args) {
question1();
System.out.println();
question2();
}
}
- 可重入锁的本质:同一线程可以多次获取同一个锁,通过计数器记录持有次数
- 两种实现方式:
synchronized关键字和ReentrantLock类 - 主要应用场景:递归调用、方法嵌套、继承中的锁传递
- ReentrantLock优势:提供更多特性(可中断、可超时、公平锁等)
- 使用注意事项:必须成对使用lock/unlock,确保释放锁
这些案例涵盖了可重入锁的主要使用场景,从简单的递归到实际的项目应用,希望能帮助你深入理解可重入锁的机制和用法。