我试图编写一个代码,显示ReentrantLock的不公平性(将ctor传递给fair = false时)。令我惊讶的是,ReentrantLock非常公平。
我的测试具有以下逻辑:产生20个线程,这些线程的“id”从0到19。所有线程共享一个ReentrantLock。然后,按时间顺序:
我期望有时,一个线程在另一个实际上正在等待更长时间的线程之前获得锁。但这永远不会发生
代码:
package jma.test;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantLock;
class ThreadTest extends Thread {
private final int id;
private final int totalNbThreads;
private final ReentrantLock lock1;
private final LinkedList<Integer> checkOrder;
ThreadTest(int id, int totalNbThreads, ReentrantLock lock, LinkedList<Integer> checkOrder) {
this.id = id;
this.totalNbThreads = totalNbThreads;
this.lock1 = lock;
this.checkOrder = checkOrder;
}
public void run() {
try {
// This if is to force threads to get to lock() call below in order of their ids.
// Thread 0 should call lock() first, then threads 1, 2, 3, 4 ...
if (this.id == 1) {
while (!lock1.isLocked()) {
// wait for thread 0 to lock it
}
} else if (this.id > 1) {
while (lock1.getQueueLength() != (this.id - 1)) {
// íf we are thread n, we wait for thread 1 to n-1 to enter the wait queue.
}
}
lock1.lock();
if (this.id == 0) {
while (lock1.getQueueLength() != (totalNbThreads - 1)) {
// Wait for all other threads to bloc on lock1.lock() before releasing lock
}
}
checkOrder.add(this.id);
} finally {
lock1.unlock();
}
}
}
public class Main {
private static final int NB_THREADS = 20; // at least 2
// change the boolean to switch between fair or not-fair lock
private static final ReentrantLock lock = new ReentrantLock(false);
private static boolean isLockFair() {
Queue<Thread> allThreads = new LinkedList<>();
LinkedList<Integer> checkOrder = new LinkedList<>();
for (int id=0; id < NB_THREADS; id++) {
allThreads.add(new ThreadTest(id, NB_THREADS, lock, checkOrder));
}
for (Thread t : allThreads) {
t.start();
}
for (Thread t : allThreads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int previous = -1;
for (int i : checkOrder) {
if (i != previous + 1) {
System.out.println("not fair: " + i + " got the lock after " + previous);
return false;
}
previous = i;
}
return true;
}
public static void main(String[] args) {
int ctrUnfair = 0;
int nbTest = 10000;
for (int i=0; i<nbTest; i++) {
if (!isLockFair())
ctrUnfair++;
}
System.out.println("unfairness: " + ctrUnfair + "/" + nbTest);
}
}
我假设,因为释放锁的线程不会尝试再次获取它,所以在调用unlock时,正在运行的线程与被阻止的线程之间没有并发性,因此将获得锁的线程必然来自等待队列,而等待队列的实现可能是一个FIFO。是解释吗?
最佳答案
以我的理解,这是在以下情况下发生的。
所有这些都来自源代码AQS和ReentrantLock。
回到您的代码,在线程0可以解锁之前,所有其他线程都在等待队列中,因此与上面的情况有所不同。希望这会有所帮助,如果我错了,请告诉我。