ReentrantLock(重入鎖)的公平性

在ReentrantLock中,對於公平和非公平的定義是經過對同步器AbstractQueuedSynchronizer的擴展加以實現的java

非公平的獲取語義:

image

公平的獲取語義:

image

比較非公平的獲取,僅加入了當前線程(Node)以前是否有前置節點在等待的判斷ide

編寫一個測試來觀察公平和非公平鎖在獲取鎖時的區別,在測試用例中定義了內部
類ReentrantLock2,該類主要公開了getQueuedThreads()方法,該方法返回正在等待獲取鎖的線
程列表,因爲列表是逆序輸出,爲了方便觀察結果,將其進行反轉測試

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FairAndUnfairTest {

    private static Lock fairLock = new ReentrantLock2(true);
    private static Lock unfairLock = new ReentrantLock2(false);

    public static void main(String[] args) throws Exception {
//        fair();
        unfair();
    }
    public static void fair() {

        System.out.println("fair version");
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Job(fairLock)) {
                public String toString() {
                    return getName();
                }
            };
            thread.setName("" + i);
            thread.start();
        }
        // sleep 5000ms
    }


    public static void unfair() {
        System.out.println("unfair version");
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Job(unfairLock)) {
                public String toString() {
                    return getName();
                }
            };
            thread.setName("" + i);
            thread.start();
        }
        // sleep 5000ms
    }


    private static class Job implements Runnable {

        private Lock lock;
        public Job(Lock lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            for (int i = 0; i < 2; i++) {
                lock.lock();
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("Lock by:"
                            + Thread.currentThread().getName() + " and "
                            + ((ReentrantLock2) lock).getQueuedThreads()
                            + " waits.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }


    private static class ReentrantLock2 extends ReentrantLock {
        public ReentrantLock2(boolean fair) {
            super(fair);
        }
        private static final long serialVersionUID = 1773716895097002072L;
        public Collection<Thread> getQueuedThreads() {
            List<Thread> threads = new ArrayList<Thread>(super.getQueuedThreads());
            Collections.reverse(threads);
            return threads;
        }
    }
}

調用非公平方法,返回結果:this

unfair version
Lock by:0 and [1, 2, 3, 4] waits.
Lock by:0 and [1, 2, 3, 4] waits.
Lock by:1 and [2, 3, 4] waits.
Lock by:1 and [2, 3, 4] waits.
Lock by:2 and [3, 4] waits.
Lock by:2 and [3, 4] waits.
Lock by:3 and [4] waits.
Lock by:3 and [4] waits.
Lock by:4 and [] waits.
Lock by:4 and [] waits.spa

調用公平方法,返回結果:線程

fair version
Lock by:0 and [1, 2, 4, 3] waits.
Lock by:1 and [2, 4, 3, 0] waits.
Lock by:2 and [4, 3, 0, 1] waits.
Lock by:4 and [3, 0, 1, 2] waits.
Lock by:3 and [0, 1, 2, 4] waits.
Lock by:0 and [1, 2, 4, 3] waits.
Lock by:1 and [2, 4, 3] waits.
Lock by:2 and [4, 3] waits.
Lock by:4 and [3] waits.
Lock by:3 and [] waits.code

能夠明顯看出,在非公平獲取的過程當中,「插隊」現象很是嚴重,後續獲取鎖的線程根本不顧及sync隊列中等待的線程,而是能獲取就獲取。反觀公平獲取的過程,鎖的獲取就相似線性化的blog

相關文章
相關標籤/搜索