ReentrantLock源码之公平锁与非公平锁

版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.csdn.net/lixingtao0520 https://blog.csdn.net/lixingtao0520/article/details/83280425

ReentrantLock类与Synchronized关键字的主要区别之一就是可以实现公平锁和非公平锁。我们看下ReentrantLock类是如果实现公平锁与非公平锁的。

1、非公平锁的实现

ReentrantLock默认构造函数为非公平锁(为什么?因为非公平锁的实现可以减少线程的切换,提高执行效率。)

public ReentrantLock() {
        sync = new NonfairSync();
    }

ReentrantLock中含有一个内部类NonfairSync,如下

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        /**
         * 非公平lock方法。首先,直接调用AQS的compareAndSetState方法,原子设置同步状态的值。
         *      True,表明获取锁成功,设置AQS的成员变量独占锁的拥有者exclusiveOwnerThread为当前        
         *         线程
         *      False,获取锁失败。调用AQS的acquire方法。
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

         //实现父类的tryAcquire方法。尝试以独占模式获取同步状态。
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

ReentrantLock在调用lock方法时,其实调用的是NonfairSync 类的lock方法,此方法先调用AQS的模板方法compareAndSetState来原子的设定同步状态的值,如果成功,则表明获取锁成功,失败,则调用AQS的模板方法acquire()。调用AQS的模板方法acquire()如下

//该方法调用NonfairSync类中重写的tryAcquire方法
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();// 如果没有获取到同步状态,并且最终线程的中断状态为true,则会调用selfInterrupt方法进行线程中断。
    }

acquire方法是AQS实现的模板方法。tryAcquire方法需要具体的子类进行实现,这个方法一会再看。先看两个方法addWaiter()和acquireQueued() 此两个方法均为AQS的模板方法,分别如下

//addWaiter方法是AQS的模板方法,实现创建节点并进入同步队列。
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
//acquireQueued是AQS的模板方法,当前线程会根据公平原则来进行阻塞等待,直到获取到同步状态位置。并且会返回在阻塞过程中线程是否被中断过。
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //死循环,阻塞等待
            for (;;) {
                 //获取当前线程节点的前继节点,如果是首节点,并且获取成功,则直接返回中断状态
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

现在来看AQS的子孙类NonfairSync实现的tryAcquire方法

 //实现父类的tryAcquire方法。尝试以独占模式获取同步状态。
 protected final boolean tryAcquire(int acquires) {
     return nonfairTryAcquire(acquires);
 }

nonfairTryAcquire()方法在AQS的子类Sync类中已实现,如下。(为什么放到Sync类中?

//Sync实现的nonfairTryAcquire方法。
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {//同步状态为0,则再次设置同步状态,成功,则返回True
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//同步状态不为0,当占用锁的线程为当前线程时,重入,返回成功。
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

2、公平锁的实现

公平锁调用Lock方法时,并没有上来调用compareAndSetState方法(因为不知道同步队列中是否有线程等待)。首先调用AQS的模板方法acquire方法,addWaiter方法与acquireQueued调用的逻辑相同,主要看公平锁版本tryAcquire方法的实现。

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        final void lock() {
//注意这里与公平锁实现的不同,公平锁不能直接调用CAS方法,因为公平锁需要确认同步队列中是否有等待获取同步状态的线程
            acquire(1);
        }
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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,这是实现公平锁和非公平锁的关键。hasQueuedPredecessors方法是查询同步队列中是否有等待获取锁的线程,只有返回false时,才会设置同步状态的值。否则,加入同步队列阻塞等待获取锁。提现公平锁的特点。

参考文章:

(1)JUC--AQS源码分析(二)同步状态的获取与释放

(2)《Java并发编程的艺术》第五章Java中的锁。

猜你喜欢

转载自blog.csdn.net/lixingtao0520/article/details/83280425