重入锁有哪些重要的方法?请列举并说明其作用。
参考回答
ReentrantLock
是 Java 提供的一个用于实现可重入锁的类,它位于 java.util.concurrent.locks
包中。与 synchronized
关键字相比,ReentrantLock
提供了更多的灵活性,比如显式的锁获取和释放、支持中断、支持超时等特性。
以下是 ReentrantLock
类中几个重要的方法及其作用:
lock()
:- 作用:获取锁,如果锁未被其他线程持有,当前线程会获得锁并进入临界区。如果锁已被其他线程持有,当前线程会被阻塞,直到锁可用。
- 使用场景:在代码块或方法中显式地加锁,确保在执行期间不会被其他线程打断。
unlock()
:- 作用:释放锁,允许其他线程获取该锁。必须由持有锁的线程调用,否则会抛出
IllegalMonitorStateException
。 - 使用场景:确保锁的释放,通常放在
finally
块中,防止因异常导致锁无法释放。
- 作用:释放锁,允许其他线程获取该锁。必须由持有锁的线程调用,否则会抛出
tryLock()
:- 作用:尝试获取锁。如果锁未被其他线程持有,当前线程立即获得锁并返回
true
;如果锁已被持有,则立即返回false
,不会被阻塞。 - 使用场景:用于不希望被阻塞的场景,例如,如果获取锁失败,线程可以做其他工作或尝试其他操作。
- 作用:尝试获取锁。如果锁未被其他线程持有,当前线程立即获得锁并返回
tryLock(long time, TimeUnit unit)
:- 作用:尝试在指定的时间内获取锁。如果在指定的时间内未能获取锁,则返回
false
;如果锁可用,线程会获取锁并返回true
。 - 使用场景:用于需要在一定时间内尝试获取锁的场景,避免线程无休止地等待。
- 作用:尝试在指定的时间内获取锁。如果在指定的时间内未能获取锁,则返回
lockInterruptibly()
:- 作用:类似于
lock()
,但是它可以响应中断。如果当前线程在获取锁时被中断,抛出InterruptedException
,不再继续等待锁。 - 使用场景:用于需要响应中断的场景,例如某些操作可能在某些条件下超时或需要取消。
- 作用:类似于
newCondition()
:- 作用:返回一个新的
Condition
对象,可以用来在ReentrantLock
下实现线程间的通信。通过await()
、signal()
、signalAll()
等方法进行线程间的协调。 - 使用场景:用于线程间的等待/通知机制,比如生产者消费者模式。
- 作用:返回一个新的
详细讲解与拓展
1. lock()
方法
lock()
方法用于获取锁,当锁没有被其他线程占用时,当前线程会成功获得锁并继续执行;如果锁已被其他线程持有,当前线程会一直等待直到锁可用。
注意事项:
- 使用
lock()
方法时,应该保证配套的unlock()
方法在操作完成后释放锁,防止出现死锁。
示例:
2. unlock()
方法
unlock()
方法必须由持有锁的线程调用,否则会抛出 IllegalMonitorStateException
。因此,使用 unlock()
时需要确保当前线程持有锁。
使用建议:
unlock()
应该放在finally
块中,保证即使发生异常也能正确释放锁,防止出现死锁。
示例:
3. tryLock()
方法
tryLock()
方法尝试获取锁,如果锁已被其他线程持有,当前线程不会被阻塞,而是立即返回 false
。
适用场景:
- 不想让线程一直阻塞,能够快速尝试获取锁并决定是否执行后续操作。
- 例如,可以定期尝试锁,或者做一些其他工作,避免一直等待锁的释放。
示例:
4. tryLock(long time, TimeUnit unit)
方法
该方法在指定的时间内尝试获取锁,如果在给定的时间内成功获取锁,返回 true
;如果超时,则返回 false
。
适用场景:
- 在需要有限时间等待锁的情况下使用,避免线程永远阻塞。
- 例如,任务需要在一定时间内完成,如果不能获取锁,则退出。
示例:
5. lockInterruptibly()
方法
lockInterruptibly()
方法与 lock()
类似,不同之处在于它可以响应中断。如果线程在等待锁的过程中被中断,方法会抛出 InterruptedException
。
适用场景:
- 当线程可能在等待锁时需要响应中断(例如长时间等待时需要进行取消操作)时使用。
示例:
6. newCondition()
方法
newCondition()
方法返回一个 Condition
对象,使用它可以实现线程间的协调(等待/通知机制)。例如,生产者消费者模式中,生产者生产数据后通知消费者消费,反之亦然。
适用场景:
- 需要线程间通信的场景,如在一个线程完成某些操作后唤醒另一个线程。
示例:
总结
ReentrantLock
提供了丰富的功能来处理并发问题,适用于高并发环境下需要细粒度控制的场景。常用的方法包括:
lock()
和unlock()
:显式获取和释放锁,确保线程同步。tryLock()
:尝试获取锁,不会阻塞线程。tryLock(long time, TimeUnit unit)
:尝试在指定时间内获取锁,适用于时间限制的场景。lockInterruptibly()
:支持中断的锁获取方法,适合长时间等待的场景。newCondition()
:用于线程间协调的Condition
对象,支持复杂的等待和通知机制。