我试图编写一个代码,显示ReentrantLock的不公平性(将ctor传递给fair = false时)。令我惊讶的是,ReentrantLock非常公平。

我的测试具有以下逻辑:产生20个线程,这些线程的“id”从0到19。所有线程共享一个ReentrantLock。然后,按时间顺序:

  • 线程0锁定锁。
  • lock()上的线程1至19块,顺序为1、2、3,..然后19
  • 线程0解锁锁。这是对公平性的第一个测试,如果锁是公平的,线程1应该在此之后得到它
  • 当线程1拥有锁时,他也将其释放。第二个公平性测试:线程2现在应该得到它。

  • 我期望有时,一个线程在另一个实际上正在等待更长时间的线程之前获得锁。但这永远不会发生

    代码:
    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。是解释吗?

    最佳答案



    以我的理解,这是在以下情况下发生的。



    所有这些都来自源代码AQSReentrantLock

    回到您的代码,在线程0可以解锁之前,所有其他线程都在等待队列中,因此与上面的情况有所不同。希望这会有所帮助,如果我错了,请告诉我。

    10-05 18:52