关于ReentrantLock中公平锁与非公平锁的一点理解

公平锁

公平锁的主要函数调用轨迹:

  1. ReentrantLock类:lock()

  2. ReentrantLock类的静态内部类Sync(继承自AbstractQueuedSynchronizer类):acquire(int arg)

  3. FairSync类:tryAcquire(int acquires)

非公平锁

公平锁的主要函数调用轨迹:

  1. ReentrantLock类:lock()

  2. ReentrantLock类的静态内部类Sync(继承自AbstractQueuedSynchronizer类):acquire(int arg)

  3. Sync类:nonfairTryAcquire(int acquires)

从上面的主要函数调用轨迹可以看到,公平锁与非公平锁的区别仅在第三点,看JDK源码也可以发现,事实确实如此。

主要代码分析

​
​/***************************公平与非公平的公共部分***********************************/
//Sync有两种实现方式:公平、非公平
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

//1、先尝试获取锁,若成功则函数直接结束,即tryAcquire返回true
//2、若获取失败,则将当前线程构造成节点,并调用addWaiter方法将节点放到AQS尾部
//3、调用acquireQueued方法不断循环获取同步状态
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//!!!!!!!!!!!!!!!!!!!!!!!!!!!
//注意这个函数,它对与理解下面结论十分重要
final boolean acquireQueued(final Node node, int arg) {
        boolean interrupted = false;
        try {
            for (;;) {
                final Node p = node.predecessor();
//当且仅当 当前节点的前驱节点为头结点时才会调用tryAcquire方法获取同步状态
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node))
                    interrupted |= parkAndCheckInterrupt();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            if (interrupted)
                selfInterrupt();
            throw t;
        }
    }


/***************************公平与非公平的不同之处**********************************/
//先看公平方式
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
//重写了tryAcquire方法
        @ReservedStackAccess
        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;
        }
    }
//附上hasQueuedPredecessors方法
public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
//当队列为空或者队列不为空但head节点的下一节点就是当前线程时返回false
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

//非公平方式
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
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;
        }

​

​

下面仅个人理解,欢迎指正:

上面的代码中,ReentrantLock类的静态内部类Sync继承自AbstractQueuedSynchronizer类,在Sync类中仅对AbstractQueuedSynchronizer类中的TryAcquire方法分别进行了公平性实现(TryAcquire)和非公平性实现(nonFairTryAcquire),因此获取锁的过程中所涉及的其他函数均直接调用了其父类的函数。在这样的前提下,通过上述代码可以发现:

公平锁和非公平锁中的“公平性”仅体现在首次获取锁上(不包含重入情况),原因见下:

  1. 线程首次获取锁时,调用了TryAcquire方法,在这里公平锁要判断是否存在前驱节点,而非公平锁不需要判断,因此公平锁遵循“先来后到”,非公平锁不需要;
  2. 若首次没有成功获取到锁,那么无论是公平锁还是非公平锁都要被放入同步队列中,根据队列的FIFO特点以及acquireQueued逻辑(只有前驱节点为头结点的线程节点才能够获取锁)来看,无论是公平锁还是非公平锁都只能根据进入队列的先后顺序出队列。也就是说,在同步队列中时,非公平锁也得遵守“先来后到”。不过,公平锁在同步队列不为空的情况下会直接进入同步队列,而非公平锁会先尝试获取锁,失败之后才会进入同步队列。

Lock方法和TryLock方法的区别:

如上面所说,Lock方法会调用AQS的acquire方法,也就意味着若当前线程在获取锁失败的时候会进入同步队列(阻塞状态)。下面我们来看TryLock方法源码:

public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

很明显TryLock直接调用了nonfairTryAcquire方法,也就是说当前线程在获取锁失败的时候会直接返回false而不会进入阻塞态。

也正是因为TryLock的存在使得ReentrantLock实现非阻塞获取锁(区别于Sychronized)。

 
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值