图解Java ReentrantLock公平锁和非公平锁的实现攻略
什么是ReentrantLock
ReentrantLock是一个可重入锁,也称为互斥锁,它比Java原生的synchronized更加灵活,支持公平锁和非公平锁,并且可以通过tryLock方法尝试获取锁,给予更好的控制和扩展。
公平锁和非公平锁
公平锁和非公平锁都是指ReentrantLock的获取锁机制。公平锁是按照请求锁的顺序排队,先请求锁的先获得锁,而非公平锁则不保证请求锁的顺序,也就是说后来的线程可能会在先前线程的前面请求到锁。
公平锁实现
公平锁的实现是基于FIFO队列的,通过一个带有head和tail指针的FIFO队列来维护请求锁的顺序,在每个节点都保存了前驱节点的信息,新的线程请求锁时,会将其加入到队列的末尾,且只有当前持有锁的线程释放锁的时候,才唤醒队列中的下一个线程。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
private static final class FairSync extends Sync {
// 公平锁使用FIFO队列
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 如果锁没有被占用
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
// 没有等待线程,直接将锁占用,并设置独占线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 如果该线程之前已经获得了该锁(可重入)
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
非公平锁实现
与公平锁不同,非公平锁的实现没有排序的过程,它总是尝试获取锁,如果失败就进入等待状态,直到成功获取锁或者线程被中断或超时等不可抗力情况发生。
private static final class NonfairSync extends Sync {
// 非公平锁使用CAS操作直接尝试获取锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
示例
下面我们通过两个示例来说明公平锁和非公平锁的区别。
假设有两个线程A和B,并且有一个非公平锁lock。
示例1:使用公平锁
ReentrantLock fairLock = new ReentrantLock(true);
fairLock.lock();
System.out.println("A acquire fairLock");
fairLock.lock();
System.out.println("B acquire fairLock");
fairLock.unlock();
System.out.println("A release fairLock");
fairLock.unlock();
System.out.println("B release fairLock");
输出结果为:
A acquire fairLock
B acquire fairLock
A release fairLock
B release fairLock
公平锁保证线程请求锁的顺序,所以期望的输出结果就是先有A取得锁,再有B取得锁,最后A释放锁,B也随后释放锁。
示例2:使用非公平锁
ReentrantLock nonfairLock = new ReentrantLock();
nonfairLock.lock();
System.out.println("A acquire nonfairLock");
nonfairLock.lock();
System.out.println("B acquire nonfairLock");
nonfairLock.unlock();
System.out.println("A release nonfairLock");
nonfairLock.unlock();
System.out.println("B release nonfairLock");
输出结果为:
A acquire nonfairLock
B acquire nonfairLock
A release nonfairLock
B release nonfairLock
由于非公平锁没有按照请求锁的顺序进行排序,所以线程B也可以在A获得锁后立即尝试获取,成功获得锁,然后先释放锁,线程A再接着去释放锁。
结论
公平锁显然比非公平锁更加公正,能够避免饥饿现象,但是由于需要排序和维护队列,开销会更大,而非公平锁相对更加简单和高效,但是有可能会出现饥饿情况。所以选择哪一种锁取决于实际应用场景和需求。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:图解Java ReentrantLock公平锁和非公平锁的实现 - Python技术站