概述
之前一直对公平锁和非公平锁的概念不是特别清楚,简单理解为只要当前资源被加锁,之后的请求都会搁置到队列中公平锁就是严格按照FIFO(先进先出)的规矩办事。非公平锁就是不遵守这个先进先出的规矩,恶性竞争资源。在看AQS(AbstractQueuedSyncronizer)的源码中打破这种不太准确的认知.
接下来我会用ReentrantLock的源码配合着阐述我理解的公平锁和非公平锁。
公平锁: FairSync,就是当前资源被加锁后,其他所有请求线程按照请求的先后顺序搁置到queue中,当锁被释放放掉,然后严格的按照先进先出的原则一个一个加锁。
就比如生活中去超市购物后买单,只有一个收银台,这个收银台也只能服务一个顾客。当买单的人特别多的时候,大家就需要按照先来后到排队买单。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//这里做两件事情,因为是AQS源码就不具体展开
//1:判断下能不能获得锁,下面的tryAcquire(int acquires) 就是其中实现方法之一
//2: 如果不能获取锁就加入到等待队列中
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
//c==0表示当前资源没有被加锁
if (c == 0) {
//确保当前线程一定是队列中的第一个线程并且成功把state修改成1
if (!hasQueuedPredecessors() &&
//当state!=0的时候就表示加锁成功
compareAndSetState(0, acquires)) {
//设置当前线程为排它锁的拥有者
setExclusiveOwnerThread(current);
return true;
}
}
//如果state!=0,但是排它锁的拥有者就是当前线程,这个时候就是可重入锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
其实这里就定好排队买单的规矩,这段源码还额外看到了可重入锁的原理。但是我们的重点是公平锁,所**以我们接着看源码如何保证先进先出的排队顺序 hasQueuedPredecessors()**这里大家可以把Node理解成一个Thread的封装。
public final boolean hasQueuedPredecessors() {
//队列中最后一个节点
Node t = tail;
//队列中第一个节点
Node h = head;
Node s;
//这里主要判断是不是队列中的头结点
//说下自己的理解,未必会正确
//情况1:等待队列中只有1个node节点,这个时候tail != head 返回false 符合需要
//情况2: 等待队列中不止一个node节点,此时h.next == null 肯定会false,之所以需要(s = h.next) == null
//就是确保后面的判断表达式不会空指针异常,要接着判断 s.thread != Thread.currentThread()
//也就是这种场景最关键的是最后面的判断,如果当前node的thread 是当前线程,肯定是head节点无疑
// s.thread != Thread.currentThread()返回false,也是符合head节点的需求。
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
这段代码就是收银员得确定下你是不是排在最前面的买单客户,这样就保证了获取锁的公平性!
非公平锁:NonfairSync 非公平的意思就是管你三七二十一,我先尝试给共享资源加锁,如果加锁成功就阻塞其他线程(因为其他线程都在队列中排队,这个时候就特别的霸道而显得不公平),如果是共享资源上已经被加锁了,这个时候在进入队列的时候还要再判断下资源有没有被释放掉能不能加锁,两次尝试加锁都失败再霸道也没用了,就只能老老实实去队列尾部排队!
还是去超市购物后买单,只有一个收银台,这个收银台也只能服务一个顾客。当买单的人特别多,大家都排着队等着。这个时候来了个壮汉,仗着自己高大枉顾排队的游戏规则,直接跑到收银台看有没有人正在买单,如果没有人正在买单就直接插队买单。如果看了两眼还是有人正在买单,那就规规矩矩到队尾排队。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//先尝试加锁
if (compareAndSetState(0, 1))
//加锁成功直接把当前线程设置成排它锁的拥有者
setExclusiveOwnerThread(Thread.currentThread());
else
//已经有别的线程加锁了,插队加锁失败
//1:判断下能不能获得锁,下面的tryAcquire(int acquires) 就是其中实现方法之一
//2: 如果不能获取锁就加入到等待队列中
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
非公平tryAcquire()这个方法和公平锁的tryAcquire()大同小异,只有一个地方不一样
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//公平锁在这里需要判断是不是队列中的头结点 head node
//非公平锁压根不管,上来直接尝试加锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//这里可重入锁的原理,跟公平锁一样
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
好了,到了这里相信公平锁和非公平锁应该有点明白了,不会像我之前一样理解错误了!
最后
以上就是诚心雨为你收集整理的真的理解公平锁和非公平锁嘛?的全部内容,希望文章能够帮你解决真的理解公平锁和非公平锁嘛?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复