0Pricing
Java Academy · 课时

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. ReentrantLock 与 synchronized
  2. 用于读写场景的 ReadWriteLock
  3. 原子变量:无锁更新
  4. StampedLock 与乐观读取
← 返回 Java Academy