重入锁有哪些重要的方法?请列举并说明其作用。

参考回答

ReentrantLock 是 Java 提供的一个用于实现可重入锁的类,它位于 java.util.concurrent.locks 包中。与 synchronized 关键字相比,ReentrantLock 提供了更多的灵活性,比如显式的锁获取和释放、支持中断、支持超时等特性。

以下是 ReentrantLock 类中几个重要的方法及其作用:

  1. lock()
    • 作用:获取锁,如果锁未被其他线程持有,当前线程会获得锁并进入临界区。如果锁已被其他线程持有,当前线程会被阻塞,直到锁可用。
    • 使用场景:在代码块或方法中显式地加锁,确保在执行期间不会被其他线程打断。
  2. unlock()
    • 作用:释放锁,允许其他线程获取该锁。必须由持有锁的线程调用,否则会抛出 IllegalMonitorStateException
    • 使用场景:确保锁的释放,通常放在 finally 块中,防止因异常导致锁无法释放。
  3. tryLock()
    • 作用:尝试获取锁。如果锁未被其他线程持有,当前线程立即获得锁并返回 true;如果锁已被持有,则立即返回 false,不会被阻塞。
    • 使用场景:用于不希望被阻塞的场景,例如,如果获取锁失败,线程可以做其他工作或尝试其他操作。
  4. tryLock(long time, TimeUnit unit)
    • 作用:尝试在指定的时间内获取锁。如果在指定的时间内未能获取锁,则返回 false;如果锁可用,线程会获取锁并返回 true
    • 使用场景:用于需要在一定时间内尝试获取锁的场景,避免线程无休止地等待。
  5. lockInterruptibly()
    • 作用:类似于 lock(),但是它可以响应中断。如果当前线程在获取锁时被中断,抛出 InterruptedException,不再继续等待锁。
    • 使用场景:用于需要响应中断的场景,例如某些操作可能在某些条件下超时或需要取消。
  6. newCondition()
    • 作用:返回一个新的 Condition 对象,可以用来在 ReentrantLock 下实现线程间的通信。通过 await()signal()signalAll() 等方法进行线程间的协调。
    • 使用场景:用于线程间的等待/通知机制,比如生产者消费者模式。

详细讲解与拓展

1. lock() 方法

lock() 方法用于获取锁,当锁没有被其他线程占用时,当前线程会成功获得锁并继续执行;如果锁已被其他线程持有,当前线程会一直等待直到锁可用。

注意事项

  • 使用 lock() 方法时,应该保证配套的 unlock() 方法在操作完成后释放锁,防止出现死锁。

示例

ReentrantLock lock = new ReentrantLock();

lock.lock(); // 获取锁
try {
    // 执行临界区操作
} finally {
    lock.unlock(); // 确保释放锁
}
Java

2. unlock() 方法

unlock() 方法必须由持有锁的线程调用,否则会抛出 IllegalMonitorStateException。因此,使用 unlock() 时需要确保当前线程持有锁。

使用建议

  • unlock() 应该放在 finally 块中,保证即使发生异常也能正确释放锁,防止出现死锁。

示例

lock.lock(); // 获取锁
try {
    // 执行临界区操作
} finally {
    lock.unlock(); // 确保锁被释放
}
Java

3. tryLock() 方法

tryLock() 方法尝试获取锁,如果锁已被其他线程持有,当前线程不会被阻塞,而是立即返回 false

适用场景

  • 不想让线程一直阻塞,能够快速尝试获取锁并决定是否执行后续操作。
  • 例如,可以定期尝试锁,或者做一些其他工作,避免一直等待锁的释放。

示例

if (lock.tryLock()) {
    try {
        // 执行临界区操作
    } finally {
        lock.unlock();
    }
} else {
    // 锁获取失败,执行其他操作
}
Java

4. tryLock(long time, TimeUnit unit) 方法

该方法在指定的时间内尝试获取锁,如果在给定的时间内成功获取锁,返回 true;如果超时,则返回 false

适用场景

  • 在需要有限时间等待锁的情况下使用,避免线程永远阻塞。
  • 例如,任务需要在一定时间内完成,如果不能获取锁,则退出。

示例

boolean isLocked = lock.tryLock(1000, TimeUnit.MILLISECONDS);
if (isLocked) {
    try {
        // 执行临界区操作
    } finally {
        lock.unlock();
    }
} else {
    // 获取锁失败,做其他事情
}
Java

5. lockInterruptibly() 方法

lockInterruptibly() 方法与 lock() 类似,不同之处在于它可以响应中断。如果线程在等待锁的过程中被中断,方法会抛出 InterruptedException

适用场景

  • 当线程可能在等待锁时需要响应中断(例如长时间等待时需要进行取消操作)时使用。

示例

try {
    lock.lockInterruptibly(); // 尝试获取锁
    try {
        // 执行临界区操作
    } finally {
        lock.unlock();
    }
} catch (InterruptedException e) {
    // 处理中断
    Thread.currentThread().interrupt(); // 恢复中断状态
}
Java

6. newCondition() 方法

newCondition() 方法返回一个 Condition 对象,使用它可以实现线程间的协调(等待/通知机制)。例如,生产者消费者模式中,生产者生产数据后通知消费者消费,反之亦然。

适用场景

  • 需要线程间通信的场景,如在一个线程完成某些操作后唤醒另一个线程。

示例

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

lock.lock();
try {
    while (someCondition) {
        condition.await(); // 线程等待
    }
    // 执行临界区操作
    condition.signal(); // 唤醒等待的线程
} finally {
    lock.unlock();
}
Java

总结

ReentrantLock 提供了丰富的功能来处理并发问题,适用于高并发环境下需要细粒度控制的场景。常用的方法包括:

  1. lock()unlock():显式获取和释放锁,确保线程同步。
  2. tryLock():尝试获取锁,不会阻塞线程。
  3. tryLock(long time, TimeUnit unit):尝试在指定时间内获取锁,适用于时间限制的场景。
  4. lockInterruptibly():支持中断的锁获取方法,适合长时间等待的场景。
  5. newCondition():用于线程间协调的 Condition 对象,支持复杂的等待和通知机制。

发表评论

后才能评论