ReentrantLock 与 synchronized
比较 ReentrantLock 的功能(tryLock、lockInterruptibly、公平性)与 synchronized 关键字
ReentrantLock 与 synchronized 是 CoddyKit 上的免费 Java Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
同步关键字
Java 的 synchronized 关键字使用对象的内置锁(监视器)提供互斥。它简单但有所限制——没有超时、没有公平性,也不支持可中断。
class Counter {
private int count = 0;
synchronized void increment() {
count++; // only one thread at a time
}
synchronized int get() { return count; }
}
Counter c = new Counter();
// Multiple threads can safely call increment()ReentrantLock 基础
ReentrantLock 是来自 java.util.concurrent.locks 的显式锁。它提供与同步机制相同的互斥能力,但控制更加灵活。必须在 finally 块中手动释放该锁。
import java.util.concurrent.locks.*;
class SafeCounter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // always release in finally!
}
}
int get() {
lock.lock();
try { return count; }
finally { lock.unlock(); }
}
}tryLock:非阻塞获取
tryLock() 尝试获取锁而不会阻塞。如果成功则返回真;如果锁由其他线程持有,则返回假。
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) {
try {
System.out.println("Got the lock, doing work");
} finally {
lock.unlock();
}
} else {
System.out.println("Lock busy, skipping or retrying");
}带超时的 tryLock
等待指定时间以获取锁:
try {
if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
// work with protected resource
System.out.println("Acquired within 500ms");
} finally {
lock.unlock();
}
} else {
System.out.println("Timed out waiting for lock");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}lockInterruptibly
lockInterruptibly() 获取锁,除非线程被中断——这对于等待锁且可取消的任务很有用:
try {
lock.lockInterruptibly(); // throws InterruptedException if interrupted
try {
// do work
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("Interrupted while waiting for lock");
Thread.currentThread().interrupt();
}可重入性
synchronized 和 ReentrantLock 都是可重入的——已经持有锁的线程可以再次获取它,而不会发生死锁:
synchronized void outer() {
System.out.println("outer");
inner(); // same thread re-enters — OK
}
synchronized void inner() {
System.out.println("inner"); // same lock, same thread
}
// ReentrantLock:
ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.lock(); // acquire again — hold count = 2
lock.unlock(); // hold count = 1
lock.unlock(); // hold count = 0, lock released公平性策略
创建公平的 ReentrantLock,按 FIFO 顺序授予访问权——这可以防止线程饥饿,但会降低吞吐量:
// Unfair (default): no ordering guarantee, better throughput
ReentrantLock unfair = new ReentrantLock();
// Fair: threads acquire in arrival order
ReentrantLock fair = new ReentrantLock(true);
System.out.println(fair.isFair()); // true条件变量
ReentrantLock 提供 Condition 对象,比 wait()/notify() 更灵活:
ReentrantLock lock2 = new ReentrantLock();
Condition notEmpty = lock2.newCondition();
Queue<String> queue = new LinkedList<>();
// Producer
lock2.lock();
try {
queue.offer("item");
notEmpty.signal(); // wake one waiting thread
} finally { lock2.unlock(); }
// Consumer
lock2.lock();
try {
while (queue.isEmpty()) notEmpty.await(); // wait & release lock
System.out.println(queue.poll());
} finally { lock2.unlock(); }何时使用 ReentrantLock 而不是同步机制
请在以下情况下使用同步机制:
- 需要简单的互斥
- 不需要超时、公平性或可中断性
请在以下情况下使用 ReentrantLock:
- 需要 tryLock() 或带超时的 tryLock(超时)
- 需要 lockInterruptibly()
- 需要公平的顺序
- 需要多个条件变量
锁计数与监控
ReentrantLock 提供诊断方法:
ReentrantLock lock3 = new ReentrantLock();
lock3.lock();
lock3.lock(); // reentrant — hold count 2
System.out.println(lock3.getHoldCount()); // 2
System.out.println(lock3.isHeldByCurrentThread()); // true
System.out.println(lock3.isLocked()); // true
System.out.println(lock3.getQueueLength()); // 0 (no waiting threads)
lock3.unlock();
lock3.unlock(); // fully released常见错误:未持有锁就解锁
如果当前线程不持有锁却调用 unlock(),就会抛出 IllegalMonitorStateException。请始终成对使用 lock/unlock,或使用 try-finally:
ReentrantLock lock4 = new ReentrantLock();
try {
// If lock() is never called (e.g., skipped by exception before this line)
lock4.unlock(); // throws IllegalMonitorStateException
} catch (IllegalMonitorStateException e) {
System.out.println("Must lock before unlock!");
}快速检查
ReentrantLock 的哪个方法允许线程获取锁;如果锁不可用,则立即返回而不阻塞?
回顾:ReentrantLock 与同步机制
关键要点:
- 两者都提供互斥和可重入性
- ReentrantLock 额外提供:tryLock、lockInterruptibly、公平性和条件变量
- 请始终在 finally 块中释放锁
- 同步机制更简单——当其功能足够时,优先使用它
- 对于高级场景使用 ReentrantLock:超时、可中断性和多个条件
常见问题解答
「ReentrantLock 与 synchronized」课时是免费的吗?
是的 — 「ReentrantLock 与 synchronized」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「ReentrantLock 与 synchronized」这节课中我会学到什么?
比较 ReentrantLock 的功能(tryLock、lockInterruptibly、公平性)与 synchronized 关键字 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「ReentrantLock 与 synchronized」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- ReentrantLock 与 synchronized
- 用于读写场景的 ReadWriteLock
- 原子变量:无锁更新
- StampedLock 与乐观读取