ReentrantLock(重入锁)的公平性

在ReentrantLock中,对于公平和非公平的定义是通过对同步器AbstractQueuedSynchronizer的扩展加以实现的

非公平的获取语义:

image

公平的获取语义:

image

比较非公平的获取,仅加入了当前线程(Node)之前是否有前置节点在等待的判断

编写一个测试来观察公平和非公平锁在获取锁时的区别,在测试用例中定义了内部
类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;
        }
    }
}

调用非公平方法,返回结果:

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.

调用公平方法,返回结果:

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.

可以明显看出,在非公平获取的过程中,“插队”现象非常严重,后续获取锁的线程根本不顾及sync队列中等待的线程,而是能获取就获取。反观公平获取的过程,锁的获取就类似线性化的

posted @ 2016-12-06 21:35  熊猫太郎  阅读(792)  评论(0编辑  收藏  举报