重入锁(ReentrantLock)是一种可重入的互斥锁,它允许同一个线程多次获取同一个锁。重入锁的特点和好处如下:
1. 可重入性:同一个线程可以多次获取同一个锁,而不会造成死锁。当一个线程已经获取了锁之后,再次获取锁时会增加锁的持有计数,只有当锁的持有计数为0时才会真正释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try {
// 执行一些操作
methodA();
} finally {
lock.unlock();
}
}
public static void methodA() {
lock.lock();
try {
// 执行一些操作
} finally {
lock.unlock();
}
}
}
示例中,同一个线程在获取锁之后,可以多次调用方法methodA并再次获取锁,而不会造成死锁。这种可重入性允许线程在同一个锁的保护下执行多个相关的操作。
2. 公平性:重入锁可以通过构造函数指定锁的公平性,即等待时间最长的线程优先获取锁。这样可以避免线程饥饿现象,保证所有线程都能有机会获取锁。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) {
// 创建并启动多个线程
}
public static void someMethod() {
lock.lock();
try {
// 执行一些操作
} finally {
lock.unlock();
}
}
}
示例中,通过在重入锁的构造函数中传入true来创建一个公平锁。这样,等待时间最长的线程将优先获取锁。这种公平性保证了所有线程都能有机会获取锁,避免了线程饥饿现象。
3. 可中断:重入锁提供了可中断的获取锁的方式。当一个线程在等待获取锁的过程中,可以通过调用interrupt方法中断自己,从而退出等待状态。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
lock.lockInterruptibly();
// 执行一些操作
} catch (InterruptedException e) {
// 处理中断异常
} finally {
lock.unlock();
}
});
Thread t2 = new Thread(() -> {
try {
lock.lockInterruptibly();
// 执行一些操作
} catch (InterruptedException e) {
// 处理中断异常
} finally {
lock.unlock();
}
});
t1.start();
t2.start();
// 在某个时刻中断t1或t2线程
t1.interrupt();
}
}
示例中,使用lockInterruptibly方法来获取锁,并通过捕获InterruptedException来处理中断异常。这样,当某个线程在等待获取锁的过程中被中断时,可以及时退出等待状态,避免线程长时间等待锁而无法响应中断。
4. 条件变量:重入锁提供了条件变量的支持,可以通过条件变量实现线程的等待和唤醒机制。通过使用条件变量,可以更灵活地控制线程的并发执行。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionVariableExample {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private static boolean flag = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
lock.lock();
try {
while (!flag) {
try {
condition.await();
} catch (InterruptedException e) {
// 处理中断异常
}
}
// 执行一些操作
} finally {
lock.unlock();
}
});
Thread t2 = new Thread(() -> {
lock.lock();
try {
// 执行一些操作
flag = true;
condition.signal();
} finally {
lock.unlock();
}
});
t1.start();
t2.start();
}
}
示例中,创建了一个条件变量condition,并使用它在两个线程之间进行等待和唤醒操作。线程t1在获取锁之后,进入while循环,检查条件是否满足,如果条件不满足,则调用condition.await()方法将线程进入等待状态。线程t2在获取锁之后,执行一些操作,并将条件flag设置为true,然后调用condition.signal()方法唤醒等待的线程t1。
通过使用条件变量,我们可以更灵活地控制线程的并发执行。线程可以根据条件的满足与否来决定是否等待或继续执行,从而实现线程间的协调和通信。
需要注意的是,在使用条件变量时,必须先获取锁才能调用条件变量的方法。这是因为条件变量是与锁相关联的,只有持有锁的线程才能操作条件变量。
重入锁的好处包括:
1. 灵活性:重入锁提供了更灵活的锁获取和释放方式,可以适应不同的并发场景。与synchronized关键字相比,重入锁提供了更多的功能和控制选项。
2. 性能优化:重入锁的性能通常比synchronized关键字更好。重入锁采用基于CAS操作的方式来实现锁的获取和释放,相对于synchronized关键字的互斥方式,重入锁的性能更高。
3. 可中断等待:重入锁支持可中断的等待方式,可以更好地处理线程的中断请求,避免线程长时间等待锁而无法响应中断。
总而言之,重入锁是一种功能强大且灵活的锁机制,它提供了可重入性、公平性、可中断等待和条件变量等特性,可以更好地满足不同并发场景的需求,并提供更好的性能和可靠性。