在Java并发编程中,锁是一种非常重要的同步机制,用于控制多个线程对共享资源的访问。Java提供了多种锁机制,其中最常用的是ReentrantLock
,它位于java.util.concurrent.locks
包中。ReentrantLock
提供了比synchronized
关键字更灵活的锁定机制。
ReentrantLock简介
ReentrantLock
是一个可重入的互斥锁,它实现了Lock
接口。与synchronized
相比,ReentrantLock
提供了以下额外的功能:
- 公平锁:可以选择是否采用公平策略来分配锁。公平锁保证线程按照请求锁的顺序获得锁,而非公平锁则可能允许后来的线程抢先获得锁。
- 可中断的等待:线程等待锁时可以响应中断请求。
- 可超时的尝试:尝试获取锁时可以设置超时时间。
- 条件变量:支持更复杂的条件变量,允许线程等待特定条件满足后继续执行。
- 非阻塞模式:可以尝试获取锁,如果锁不可用,则返回失败而不是阻塞。
使用ReentrantLock
要使用ReentrantLock
,首先需要创建一个ReentrantLock
实例。可以使用默认构造器创建一个非公平锁,或者使用带有布尔参数的构造器来创建一个公平锁。
创建ReentrantLock
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final ReentrantLock lock = new ReentrantLock(true); // 创建公平锁
}
锁的使用
一旦创建了ReentrantLock
实例,就可以使用以下方法来锁定和解锁:
lock()
:获取锁,如果锁已经被另一个线程持有,则当前线程会阻塞直到获得锁。unlock()
:释放锁。
一个典型的使用场景是在一个方法中使用锁来保护临界区,确保同一时刻只有一个线程可以访问共享资源。下面是一个简单的例子:
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final ReentrantLock lock = new ReentrantLock(true);
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
return count;
}
}
在这个例子中,我们使用lock()
方法获取锁,然后在finally
块中释放锁,以确保即使发生异常也能正确释放锁。
条件变量
除了基本的锁定功能外,ReentrantLock
还支持条件变量。条件变量允许一个或多个线程在一个特定的点上等待,直到其他线程发出信号。这可以通过newCondition()
方法获得一个Condition
对象。
使用Condition
下面的例子展示了如何使用Condition
来等待一个特定条件满足:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean ready = false;
public void await() {
lock.lock();
try {
while (!ready) {
condition.await(); // 等待直到收到信号
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
try {
ready = true;
condition.signalAll(); // 发出信号唤醒所有等待线程
} finally {
lock.unlock();
}
}
}
在这个例子中,await()
方法会让线程等待直到ready
变为true
,而signal()
方法会设置ready
标志并唤醒等待的线程。
其他方法
除了上述方法之外,ReentrantLock
还提供了其他一些有用的方法:
tryLock()
:尝试获取锁,如果锁不可用,则立即返回。tryLock(long timeout, TimeUnit unit)
:尝试获取锁,如果锁不可用,则等待一定时间后返回。isLocked()
:检查锁是否被任何线程持有。hasQueuedThreads()
:检查是否有线程正在等待获取锁。
总结
ReentrantLock
提供了比synchronized
更强大的功能,使其成为处理复杂并发问题的强大工具。它不仅提供了更细粒度的控制,而且支持条件变量,可以实现更复杂的同步逻辑。