在Java中,Lock
接口是java.util.concurrent.locks
包中的一部分,它提供了比synchronized
关键字更灵活的锁机制。Lock
接口允许更细粒度的锁控制,支持尝试获取锁、超时获取锁、可中断获取锁等操作。Lock
用于控制对共享资源的访问,而 Condition
则用于线程间的协调。
1. Lock
接口的主要方法
Lock
接口定义了以下主要方法:
void lock()
: 获取锁。如果锁不可用,当前线程将被阻塞,直到锁可用。void lockInterruptibly()
: 获取锁,但允许在等待锁的过程中被中断。boolean tryLock()
: 尝试获取锁,如果锁可用则立即返回true
,否则返回false
。boolean tryLock(long time, TimeUnit unit)
: 尝试在指定的时间内获取锁,如果成功则返回true
,否则返回false
。void unlock()
: 释放锁。Condition newCondition()
: 返回一个与当前锁关联的Condition
对象,用于线程间的协调。
2. ReentrantLock
类
ReentrantLock
是Lock
接口的一个实现类,它是一个可重入锁,意味着同一个线程可以多次获取同一个锁。
2.1 基本用法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final Lock lock = new ReentrantLock();
public void performTask() {
lock.lock(); // 获取锁
try {
// 临界区代码
System.out.println(Thread.currentThread().getName() + " is performing the task.");
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
LockExample example = new LockExample();
Runnable task = example::performTask;
Thread thread1 = new Thread(task, "Thread-1");
Thread thread2 = new Thread(task, "Thread-2");
thread1.start();
thread2.start();
}
}
在上面的例子中,performTask
方法使用lock.lock()
获取锁,并在finally
块中释放锁,以确保锁总是被释放。
2.2 尝试获取锁
public void performTaskWithTryLock() {
if (lock.tryLock()) {
try {
// 临界区代码
System.out.println(Thread.currentThread().getName() + " is performing the task.");
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " could not acquire the lock.");
}
}
tryLock()
方法尝试获取锁,如果锁不可用,则立即返回false
,不会阻塞线程。
2.3 超时获取锁
public void performTaskWithTimeout() {
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 临界区代码
System.out.println(Thread.currentThread().getName() + " is performing the task.");
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " could not acquire the lock within the timeout.");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for the lock.");
}
}
tryLock(long time, TimeUnit unit)
方法尝试在指定的时间内获取锁,如果在指定时间内未能获取锁,则返回false
。
2.4 可中断获取锁
public void performTaskInterruptibly() {
try {
lock.lockInterruptibly();
try {
// 临界区代码
System.out.println(Thread.currentThread().getName() + " is performing the task.");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for the lock.");
}
}
lockInterruptibly()
方法允许在等待锁的过程中被中断。
2.5 锁的公平性
ReentrantLock
支持公平锁(按等待顺序分配锁),默认为非公平锁。
Lock fairLock = new ReentrantLock(true); // 创建公平锁
3. Condition 接口
Condition
接口用于线程间的协调。它类似于 Object
的 wait()
和 notify()
方法,但提供了更灵活的功能。
3.1 常用方法
方法 | 描述 |
---|---|
void await() | 等待,直到被唤醒或中断 |
void awaitUninterruptibly() | 不可中断等待 |
long awaitNanos(long nanos) | 等待指定纳秒 |
boolean await(long time, TimeUnit unit) | 超时等待 |
boolean awaitUntil(Date deadline) | 等待到指定时间 |
void signal() | 唤醒一个等待线程 |
void signalAll() | 唤醒所有等待线程 |
3.2 使用示例
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean ready = false;
public void waitForReady() throws InterruptedException {
lock.lock();
try {
while (!ready) {
condition.await(); // 等待条件满足
}
System.out.println("Ready!");
} finally {
lock.unlock();
}
}
public void setReady() {
lock.lock();
try {
ready = true;
condition.signalAll(); // 通知所有等待的线程
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ConditionExample example = new ConditionExample();
Thread waiter = new Thread(() -> {
try {
example.waitForReady();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread setter = new Thread(() -> {
try {
Thread.sleep(2000); // 模拟一些耗时操作
example.setReady();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
waiter.start();
setter.start();
try {
waiter.join();
setter.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.3 Condition
的高级用法
超时等待
public boolean awaitWithTimeout() throws InterruptedException {
lock.lock();
try {
return notEmpty.await(1, TimeUnit.SECONDS); // 最多等待1秒
} finally {
lock.unlock();
}
}
不可中断等待
public void awaitUninterruptiblyExample() {
lock.lock();
try {
notEmpty.awaitUninterruptibly(); // 即使被中断也不退出
} finally {
lock.unlock();
}
}
多条件队列
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition(); // 条件队列A
Condition conditionB = lock.newCondition(); // 条件队列B
3.4 Condition
vs wait()
/notify()
特性 | Condition | wait() /notify() |
---|---|---|
锁机制 | 必须与 Lock 配合 | 必须与 synchronized 配合 |
多条件队列支持 | ✅ 一个锁可关联多个 Condition | ❌ 只能有一个等待队列 |
超时等待 | ✅ await(long time, TimeUnit unit) | ❌ |
不可中断等待 | ✅ awaitUninterruptibly() | ❌ |
灵活性 | 高 | 低 |
4. Lock 和 Condition 的结合使用
Lock
和 Condition
通常结合使用,以实现更复杂的线程同步机制。例如,生产者-消费者问题可以使用 Lock
和 Condition
来实现。
4.1 生产者-消费者示例
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerExample {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final int[] buffer;
private int count = 0;
private int putIndex = 0;
private int takeIndex = 0;
public ProducerConsumerExample(int size) {
buffer = new int[size];
}
public void produce(int value) throws InterruptedException {
lock.lock();
try {
while (count == buffer.length) {
notFull.await(); // 等待缓冲区不满
}
buffer[putIndex] = value;
putIndex = (putIndex + 1) % buffer.length;
count++;
notEmpty.signal(); // 通知消费者可以消费
} finally {
lock.unlock();
}
}
public int consume() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 等待缓冲区不空
}
int value = buffer[takeIndex];
takeIndex = (takeIndex + 1) % buffer.length;
count--;
notFull.signal(); // 通知生产者可以生产
return value;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample(10);
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
example.produce(i);
System.out.println("Produced: " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 20; i++) {
int value = example.consume();
System.out.println("Consumed: " + value);
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5. 总结
Lock
提供了比synchronized
更灵活的锁机制,支持尝试获取锁、超时获取锁、可中断获取锁等操作。ReentrantLock
是Lock
接口的一个实现类,支持可重入锁。tryLock()
和tryLock(long time, TimeUnit unit)
方法允许尝试获取锁,避免线程阻塞。Condition
接口用于线程间的协调,类似于wait()
和notify()
,提供了比wait()
和notify()
更灵活的线程协调机制,支持多个条件队列。Lock
和Condition
通常结合使用,以实现复杂的线程同步机制,如生产者-消费者问题。
-
Lock
的优势- 提供更灵活的锁控制(可重入、可中断、超时、公平锁)。
- 需手动释放锁,确保在
finally
块中调用unlock()
。
-
Condition
的核心价值- 替代
wait()
/notify()
,支持多条件队列和精细唤醒。 - 适用于生产者-消费者、线程池任务调度等场景。
- 替代
-
使用建议
- 优先使用
Lock
和Condition
替代synchronized
,以提升灵活性和性能。 - 在复杂多线程场景中,通过多条件队列减少不必要的唤醒。
- 优先使用
通过使用Lock
和Condition
,你可以更灵活地控制多线程的同步和协调。