公平是针对锁的获取而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序。
这样就需要内部维护一个FIFO队列,Object有个wait()和notify()方法(具体可参看https://blog.csdn.net/Dongguabai/article/details/82230279),但是notify()是随机释放一个,那么我们如何保证释放的是队列中的第一个呢。那就对不同对象进行wait()和notify(),相当于notify()第一个对象,肯定就可以唤醒第一个对象等待队列中的wait()的线程,这样就实现了公平性。
package com.example.demoClient;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dongguabai
* @date 2018/11/10 16:53
*/
public class FairLock {
private boolean isLocked = false;
private Thread lockingThread = null;
//封装对象到有序集合队列
private List<QueueObject> waitingThreads = new ArrayList<QueueObject>();
public void lock() throws InterruptedException {
//每次lock()都会创建一个相应的对象
QueueObject queueObject = new QueueObject();
//由于wait(),需要同步置等待队列中
synchronized (this) {
waitingThreads.add(queueObject);
}
try {
//调用对象的doWait()方法
queueObject.doWait();
} catch (InterruptedException e) {
synchronized (this) {
waitingThreads.remove(queueObject);
}
throw e;
}
}
//同步,由于notify()
public synchronized void unlock() {
if (this.lockingThread != Thread.currentThread()) {
throw new IllegalMonitorStateException("Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
if (waitingThreads.size() > 0) {
waitingThreads.get(0).doNotify();
}
}
}
package com.example.demoClient;
/**
* @author Dongguabai
* @date 2018/11/10 16:53
*/
public class QueueObject {
//是否唤醒
private boolean isNotified = false;
public synchronized void doWait() throws InterruptedException {
//如果不是唤醒,继续wait()
while (!isNotified) {
this.wait();
}
this.isNotified = false;
}
//notify
public synchronized void doNotify() {
this.isNotified = true;
this.notify();
}
public boolean equals(Object o) {
return this == o;
}
}
接下来简单看看ReentrantLock是如何实现公平锁的。
查看公平的AQS实现:
主要是依靠与这个方法:
比较公平和非公平AQS实现的acquire()方法:
发现其实方法基本一致,但是公平锁有一个特殊的hasQueuedPredecessors()方法。大致从名称来看就是判断队列是否存在前驱节点。
有前驱结点就说明有现成应该比当前线程应该更早的获取锁,有前置就返回true。也就是说当前线程此时不应该获取锁。
===============================================================================================
以下内容转自:http://ifeve.com/starvation-and-fairness/
可重入公平锁代码:
package com.example.demoClient;
import java.util.ArrayList;
import java.util.List;
public class FairLock {
private boolean isLocked = false;
private Thread lockingThread = null;
private List<QueueObject> waitingThreads =
new ArrayList<QueueObject>();
public void lock() throws InterruptedException {
QueueObject queueObject = new QueueObject();
boolean isLockedForThisThread = true;
synchronized (this) {
waitingThreads.add(queueObject);
}
while (isLockedForThisThread) {
synchronized (this) {
isLockedForThisThread =
isLocked || waitingThreads.get(0) != queueObject;
if (!isLockedForThisThread) {
isLocked = true;
waitingThreads.remove(queueObject);
lockingThread = Thread.currentThread();
return;
}
}
try {
queueObject.doWait();
} catch (InterruptedException e) {
synchronized (this) {
waitingThreads.remove(queueObject);
}
throw e;
}
}
}
public synchronized void unlock() {
if (this.lockingThread != Thread.currentThread()) {
throw new IllegalMonitorStateException(
"Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
if (waitingThreads.size() > 0) {
waitingThreads.get(0).doNotify();
}
}
}
package com.example.demoClient;
public class QueueObject {
private boolean isNotified = false;
public synchronized void doWait() throws InterruptedException {
while (!isNotified) {
this.wait();
}
this.isNotified = false;
}
public synchronized void doNotify() {
this.isNotified = true;
this.notify();
}
@Override
public boolean equals(Object o) {
return this == o;
}
}