Java多线程 ReentrantLock互斥锁详解
在多线程编程中,为了避免线程间的竞争条件和数据不一致问题,通常需要使用互斥锁来控制线程的访问。
Java中的ReentrantLock是一种可重入的独占锁,它可以用来保护共享资源,避免多个线程同时访问造成数据不一致的问题。下面我们将详细介绍ReentrantLock的用法和注意事项。
1. ReentrantLock的基本用法
使用ReentrantLock可以分别调用lock()方法和unlock()方法来实现对共享资源的加锁和释放锁的操作。例如:
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int value = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
value++;
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
value--;
} finally {
lock.unlock();
}
}
public int getValue() {
return value;
}
}
在上面的例子中,Counter类维护了一个int类型的共享变量value,并且使用ReentrantLock来保护它的访问。在increment()和decrement()方法中,首先调用lock()方法获取锁,然后执行相应的操作,最后调用unlock()方法释放锁。这样就可以确保多个线程不会同时访问value,从而避免了数据不一致的问题。
2. ReentrantLock的高级用法
除了基本的用法外,ReentrantLock还提供了其他一些高级特性,例如条件变量和公平/非公平锁等。
2.1 条件变量
条件变量是一个线程通信的机制,可以使一个或多个线程在等待某个条件成立时被阻塞,然后在条件成立时被唤醒。例如:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class BoundedBuffer {
private final String[] buffer = new String[100];
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public void put(String element) throws InterruptedException {
lock.lock();
try {
while (count == buffer.length) {
notFull.await();
}
buffer[count] = element;
count++;
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
public String take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
String element = buffer[0];
System.arraycopy(buffer, 1, buffer, 0, count - 1);
count--;
notFull.signalAll();
return element;
} finally {
lock.unlock();
}
}
}
在上面的例子中,BoundedBuffer类维护了一个固定大小的字符串数组作为共享缓冲区,使用ReentrantLock来保护它的访问。在put()方法中,如果缓冲区已满,则调用notFull.await()来等待缓冲区有空闲位置;否则将元素加入缓冲区,并调用notEmpty.signalAll()来唤醒正在等待的读线程。在take()方法中同样如此。这样就可以实现一个简单的带条件的生产者-消费者模型。
2.2 公平/非公平锁
公平锁和非公平锁的主要区别在于对于获得锁的顺序。在公平锁中,所有线程按照请求锁的顺序依次获取锁。而在非公平锁中,线程可以在任意时刻尝试获取锁,不考虑其他线程正在等待锁的情况。例如:
import java.util.concurrent.locks.ReentrantLock;
class FairOrUnfairLock {
private ReentrantLock fairLock = new ReentrantLock(true);
private ReentrantLock unfairLock = new ReentrantLock(false);
public void fairLockTest() throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
fairLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " acquired fair lock");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
fairLock.unlock();
}
}).start();
}
}
public void unfairLockTest() throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
unfairLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " acquired unfair lock");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
unfairLock.unlock();
}
}).start();
}
}
}
在上面的例子中,FairOrUnfairLock类分别使用公平锁和非公平锁来演示它们的区别。在fairLockTest()方法中,使用公平锁,多个线程按照请求锁的顺序依次获取锁。在unfairLockTest()方法中,使用非公平锁,多个线程可以在任意时刻尝试获取锁。可以发现,在使用公平锁时,线程获得锁的顺序与它们请求锁的顺序保持一致。而在使用非公平锁时,线程可以在任意时刻尝试获取锁,因此获得锁的顺序是不确定的。
3. 总结
ReentrantLock是Java多线程编程中一种非常实用的锁机制。它提供了基本的加锁和释放锁操作,并且支持条件变量和公平/非公平锁等高级特性。合理地使用ReentrantLock可以很好地避免多线程编程中的竞争条件和数据不一致问题,提高程序的健壮性和可维护性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程 ReentrantLock互斥锁详解 - Python技术站