Java并发——ReentrantLock的公平锁与非公平锁

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/tongdanping/article/details/79580607

一、锁的公平性含义

所谓公平锁,就是在绝对时间上,先对锁发起获取请求的一定先被满足,那么这个锁是公平的,反之,则是非公平的。因为ReentrantLock的实现是通过自定义的静态内部类sync实现的,sync继承了AbstractQueuedSynchronizor抽象类,因此ReentrantLock也是实现了基于双向链表的同步队列,也就是说,如果每次都是选择队列头的Node关联的线程获取锁,那就是公平锁,而非公平锁中,队列中只要有线程妄图获取锁,一般都是很容易成功的,这种任意线程尝试获取锁成功成为插队,非公平锁允许插队。

ReentrantLock可以实现了公平锁(默认非公平锁),而synchronized只能实现非公平锁。


二、公平锁和非公平锁的实现

在ReentrantLock中我们可以找到公平锁和非公平锁获取和释放锁的实现

1、获取锁

非公平锁:为了实现ReentrantLock,因此在获取锁的时候需要首先获取锁的状态(计数器状态),如果锁没有被占用(c==0),那么任意线程可以获取同步状态,如果获取成功就把当前线程设置为独占该锁的线程。

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {  //任意线程都可以随机获取没有被占用的锁
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {  //如果该锁已经被占用,那么就要判断当前线程是不是占用锁的线程,
                int nextc = c + acquires;                     //增加计数器状态值
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;       //如果所已经被占用,且当前线程不是占用锁的线程,则获取失败
        }

公平锁:公平锁获取锁要稍微复杂一点,在获得锁的状态以后,如果锁没有被占用,那么要判断当前线程节点有没有前驱节点,如果没有前驱节点说明该节点是头结点,这时才让当前节点的线程去获取锁;其它逻辑和非公平锁是一样的。

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&            
                    compareAndSetState(0, acquires)) {   //判断当前节点是否为头结点,如果是,再让当前线程去获取锁
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

2、释放锁:

公平锁和非公平锁在释放锁上的操作时一样的,需要使用一个boolean值拉力表示锁是否被完全释放,每次调用tryRelease()方法就会使锁关联的计数器减小,直到计数器状态值为0 ,说明锁被完全释放

protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

三、公平锁和非公平锁的比较

公平锁和非公平锁都是排他锁(具有独占性);

公平锁机制按照FIFO原则,因此线程“饥饿”出现的概率小很多,而非公平锁只要线程成功获取同步状态就成功获取锁,在这个前提下,成功获取锁又释放锁的线程再次获得锁的概率非常大,这样就很容易造成线程“饥饿”。

公平锁机制没有非公平锁机制高效,因为公平锁为了保证按照FIFO原则获取锁,因此需要以大量的切换作为代价,而非公平锁切换的次数远远少于公平锁。

展开阅读全文

没有更多推荐了,返回首页