ReentrantLock
是 Java 提供的显式锁(明确地进行锁定和解锁操作),相对于 synchronized
隐式锁而言,ReentrantLock
提供了更多的功能和灵活性。
例如,它可以支持公平锁、非公平锁、尝试获取锁、可中断锁等。
示例代码
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
// 模拟一些耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
lock.lock(); // 获取锁
try {
return count;
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
讲解
-
1. 创建锁:
private final ReentrantLock lock = new ReentrantLock();
这里我们创建了一个
ReentrantLock
实例,作为计数器操作的锁。 -
2. 获取锁和释放锁:
lock.lock(); try { // 临界区代码 } finally { lock.unlock(); }
在
increment
和getCount
方法中,我们首先调用lock.lock()
方法获取锁,然后在finally
块中调用lock.unlock()
方法释放锁。这样可以确保在临界区代码执行完毕后,锁一定会被释放,防止死锁。 -
3. 处理中断:
try { count++; // 模拟一些耗时操作 Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
在临界区代码中,我们模拟了一些耗时操作,并捕获
InterruptedException
,重新设置中断状态。这是因为如果一个线程在等待、睡眠或者尝试获取锁时被中断,中断异常会被抛出,但中断状态(Thread.interrupted()
)会被清除,所以需要重新设置中断状态。 -
4. 多线程操作:
Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { counter.increment(); } }); t1.start(); t2.start();
我们创建了两个线程
t1
和t2
,每个线程都会调用increment
方法 1000 次,以模拟多线程环境下的计数器操作。 -
5. 等待线程完成:
try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); }
使用
join
方法等待两个线程执行完毕,确保主线程在两个工作线程结束后才继续执行,并最终输出计数器的值。
高级功能
-
• 公平锁与非公平锁:默认情况下,
ReentrantLock
是非公平锁,但可以通过构造函数指定为公平锁。private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
公平锁意味着等待时间最长的线程将获得对锁的访问。
-
• 可中断锁:线程在等待锁的过程中可以被中断。
try { lock.lockInterruptibly(); } catch (InterruptedException e) { // 处理中断 }
-
• 尝试获取锁:可以尝试获取锁,如果失败则不会等待,立即返回。
if (lock.tryLock()) { try { // 临界区代码 } finally { lock.unlock(); } } else { // 获取锁失败,做其他事情 }
-
• 条件变量:
ReentrantLock
可以配合Condition
使用,实现更加复杂的线程同步。Condition condition = lock.newCondition(); condition.await(); condition.signal();