前言
本篇文章将对JUC
并发包里提供的ReentrantLock
的使用进行说明。
正文
一. 非公平锁的使用
ReentrantLock
默认是非公平锁,同时获取锁资源时会存在竞争关系,不能满足先到先获取的原则。示例如下。
public class ReentrantLockTest {
@Test
public void 测试非公平等待锁() throws Exception {
// 创建锁对象,默认是非公平锁
Lock lock = new ReentrantLock();
// 创建CoundDownLatch对象,用于阻塞主线程
CountDownLatch countDownLatch = new CountDownLatch(10);
// 任务,里面存在同步代码块
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获得锁");
} finally {
countDownLatch.countDown();
lock.unlock();
}
}
};
// 创建多个线程来执行同一个任务,会存在并发竞争锁的情况
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(runnable, "线程" + i);
thread.start();
// 放弃时间片,确保上一线程已经获取锁或者已经进入阻塞状态
for (int k = 0; k < 100; k++) {
Thread.yield();
}
}
// 等待子线程执行完毕
countDownLatch.await();
}
}
上述示例中,创建ReentrantLock
时使用的是无参构造函数,即创建的锁是非公平锁。构造了十个线程,依次执行同一个任务,在任务中存在同步代码块,因为是非公平锁,所以会出现后运行的线程比先运行的线程先拿到锁的情况。
运行测试程序,打印如下。
二. 公平锁的使用
通过ReentrantLock
的有参构造函数并传入true,可以创建一个公平锁,公平锁一定满足先到先得的原则。示例如下。
public class ReentrantLockTest {
@Test
public void 测试公平等待锁() throws Exception {
// 创建锁对象,默认是非公平锁
Lock lock = new ReentrantLock(true);
// 创建CoundDownLatch对象,用于阻塞主线程
CountDownLatch countDownLatch = new CountDownLatch(10);
// 任务,里面存在同步代码块
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获得锁");
} finally {
countDownLatch.countDown();
lock.unlock();
}
}
};
// 创建多个线程来执行同一个任务,会存在并发竞争锁的情况
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(runnable, "线程" + i);
thread.start();
// 放弃时间片,确保上一线程已经获取锁或者已经进入阻塞状态
for (int k = 0; k < 100; k++) {
Thread.yield();
}
}
// 等待子线程执行完毕
countDownLatch.await();
}
}
运行测试程序,打印如下。
无论运行多少次,一定是先启动的线程先获取到锁。
三. 重入锁的使用
ReentrantLock
是可重入锁,即获取到锁的线程能够重复对锁资源进行获取。示例如下。
public class ReentrantLockTest {
@Test
public void 测试锁重入() {
// 创建锁对象,默认是非公平锁
Lock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("第一重获取锁");
lock.lock();
try {
System.out.println("第二重获取锁");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
}
}
运行测试程序,打印如下。
总结
ReentrantLock
的使用总结如下。
ReentrantLock
默认是非公平锁,即获取锁资源不满足先到先得;ReentrantLock
可以通过构造函数指定为公平锁,即先尝试获取锁资源的线程一定比后尝试获取锁资源的线程先拿到锁;ReentrantLock
是可重入锁,即获取到锁的线程能够重复对锁资源进行获取;ReentrantLock
的lock()
方法会尝试获取锁,获取失败则进入同步队列等待;ReentrantLock
的tryLock()
方法会尝试获取锁,获取成功返回true,获取失败立即返回false而不会进入同步队列等待;- 获取到锁的线程,一定要在
finally
块中调用ReentrantLock
的unlock()
方法来释放锁。