目录
ReentrantLock
特点
相对于synchronized它具备如下特点:
-
可重入性: 与synchronized一样,ReentrantLock也是可重入的,同一个线程可以多次获取同一个锁,而不会造成死锁。
-
公平性: ReentrantLock提供了公平锁和非公平锁两种锁模式。在公平模式下,锁会按照线程的请求顺序进行获取;在非公平模式下,锁不保证按照请求顺序获取。
-
条件变量支持: ReentrantLock提供了Condition接口,可以通过Condition实现线程之间的协调和通信。
-
可中断性: ReentrantLock提供了可中断的获取锁的方法,可以在获取锁的过程中响应中断信号。
-
超时获取锁: ReentrantLock提供了尝试获取锁的方法,可以在指定的时间内尝试获取锁,避免长时间等待。
语法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static final Lock lock = new ReentrantLock();
public void someMethod() {
// 获取锁
lock.lock();
try {
// 执行需要同步的代码块
} finally {
// 释放锁
lock.unlock();
}
}
}
在这个语法中:
- 首先,我们需要导入
java.util.concurrent.locks.Lock
和java.util.concurrent.locks.ReentrantLock
包。 - 创建一个
ReentrantLock
对象,通常将其声明为私有的静态常量。 - 在需要同步的方法或代码块中,首先调用
lock()
方法获取锁,然后在try
块中执行需要同步的代码。 - 使用
finally
块确保无论代码是否正常执行,都会释放锁。在finally
块中调用unlock()
方法释放锁。 - 在
lock()
和unlock()
之间的代码块即为需要同步的临界区。
需要注意的是,在使用ReentrantLock时,应该确保在获取锁后正确释放锁,以避免产生死锁或其他并发问题。通常建议将获取锁和释放锁的操作放在try-finally块中,以确保即使在发生异常的情况下也能正确释放锁。
可重入
定义
可重入(Reentrancy)指的是一个线程或进程可以再次进入自己已经持有的锁,而不会出现死锁或其他异常情况。在Java中,重入性是指同一个线程在持有锁的情况下能够再次获取该锁,而不会被自己所持有的锁所阻塞。synchronized关键字和ReentrantLock类都是可重入锁的典型代表。当一个线程已经持有了某个对象的锁时,它可以再次获取这个锁而不会被阻塞,这种机制就是可重入性的体现。
示例
public class ReentrantExample {
public synchronized void outer() {
inner();
}
public synchronized void inner() {
System.out.println("Inner method");
}
public static void main(String[] args) {
ReentrantExample example = new ReentrantExample();
example.outer();
}
}
可打断
定义
ReentrantLock 提供了可中断的获取锁的方法,即 lockInterruptibly()
方法。这意味着线程在等待锁的过程中,如果被其他线程中断,就会立即响应中断,并抛出 InterruptedException
异常。
示例
@Slf4j
public class Test14 {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
log.debug("获得锁");
}finally {
lock.unlock();
}
});
log.debug("主线程获得锁");
lock.lock();//让主线程先获得锁
t1.start();//此时t1会一直等下去
t1.interrupt();//打断t1,不让t1一直等下去
}
}
锁超时
定义
ReentrantLock提供了tryLock(long time, TimeUnit unit)
方法,用于尝试获取锁,在指定的时间内获取锁,如果获取成功则返回true,如果超时仍未获取到锁则返回false。通过这个方法,可以实现锁的超时等待。
示例
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static final Lock lock = new ReentrantLock();
public void someMethod() throws InterruptedException {
boolean locked = false;
try {
// 在指定时间内尝试获取锁
locked = lock.tryLock(5, TimeUnit.SECONDS);
if (locked) {
// 获取锁成功
System.out.println("Lock acquired successfully");
// 执行需要同步的代码块
} else {
// 获取锁超时
System.out.println("Failed to acquire lock within 5 seconds");
}
} finally {
if (locked) {
// 释放锁
lock.unlock();
}
}
}
}
在这个示例中,tryLock(5, TimeUnit.SECONDS)
方法会尝试在5秒内获取锁,如果在指定时间内获取到了锁,则返回true,表示获取成功;如果超时未获取到锁,则返回false,表示获取失败。在获取锁成功后,执行同步代码块;在finally块中释放锁,确保锁的释放。
公平锁
定义
ReentrantLock提供了公平锁和非公平锁两种模式。
在公平锁模式下,线程按照它们发出请求的顺序来获取锁,即先到先得。公平锁保证了所有等待锁的线程能够公平地获得锁,避免了某些线程长时间无法获取锁的情况。但是,由于需要维护等待队列,因此公平锁的性能可能会略微降低。
示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class FairLockExample {
private static final Lock fairLock = new ReentrantLock(true); // 创建一个公平锁
public void someMethod() {
fairLock.lock(); // 获取锁
try {
// 执行需要同步的代码块
} finally {
fairLock.unlock(); // 释放锁
}
}
}
条件变量
定义
条件变量(Condition)是Java多线程编程中的一种同步机制,它允许线程在等待某个条件成立时挂起自己,而不是忙等待。条件变量通常与锁结合使用,用于在等待某个共享资源的状态发生变化时唤醒等待的线程。
在Java中,条件变量通常与Lock对象的newCondition()方法结合使用,该方法返回一个与该Lock相关联的条件对象。通过条件对象的await()方法可以让线程等待条件的满足,而signal()或signalAll()方法可以唤醒等待的线程。
示例
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionVariableExample {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
private static boolean conditionMet = false;
public void awaitCondition() throws InterruptedException {
lock.lock();
try {
while (!conditionMet) {
condition.await(); // 等待条件满足
}
// 执行条件满足后的操作
} finally {
lock.unlock();
}
}
public void signalCondition() {
lock.lock();
try {
conditionMet = true; // 设置条件为满足状态
condition.signal(); // 唤醒等待的线程
} finally {
lock.unlock();
}
}
}
在这个示例中,awaitCondition()方法等待条件的满足,如果条件不满足则调用condition.await()方法挂起线程。signalCondition()方法设置条件为满足状态,并调用condition.signal()方法唤醒等待的线程。条件变量通常用于多个线程之间的协作,可以有效地避免忙等待的情况。