深入源码分析重入锁ReentrantLock之公平锁(与非公平锁比较)

前言

上个博客对ReentrantLock之非公平锁进行了剖析,下面我们通过源码对公平锁进行分析,并讲述它与非公平锁的区别

构造方法

 public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

这里给定参数true即可,创建公平锁的ReentrantLock的对象

源码分析

首先公平锁与非公平锁的实现就两个地方不一样,其他的地方都是一样的,下面我们将对其进行分析

先是lock方法

final void lock() {
    acquire(1);
}

①这里仔细看,其实这里就已经和非公平锁不一样了,我们把之前的非公平锁的代码拿出来对比一下

final void lock() {
    if (compareAndSetState(0, 1))//CAS操作,更新成1
//当前线程设置为持有锁的线程
setExclusiveOwnerThread(Thread.currentThread());
    else
//锁已经被其他人获得
        acquire(1);
}

可见非公平锁进来是直接通过CAS操作将状态改为1,强行占有资源,然后设置当前线程为持有锁的线程。而公平锁则通过acquire方法去获取。这里我们再对其方法进行解释,

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

 acquire方法是独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否者将会进入同步队列等待,该方法则会调用重写的tryAcquire方法

protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

②就是tryAcquire这个方法和非公平锁不一样了,注意我标注***的位置,那里开始了不同

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;
    }

再来看一下非公平锁这里的实现

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;//重入次数加1
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);//更新state
        return true; 
    }
    return false;
}

比较一下,唯一的不同在于判断条件多了hasQueuedPredecessors方法,即加入了同步队列中当前节点是否由前驱节点的判断,如果该方法返回true,则证明有线程比当前线程更早的获得锁,因此需要等待前驱线程获取并释放锁,才能继续获取锁。

然后看一下hasQueuedPredecessors方法的实现,实现了判断当前节点是否由前驱节点

ublic 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;
    return h != t &&//头是否等于尾,头不等于尾巴有东西
        ((s = h.next) == null || s.thread != Thread.currentThread());//里面持有的线程不等于当前线程
}

总结

1.其实公平锁和非公平锁的实现就两个不同一个是lock()方法一开始的实现,另外一个则是重写父类的tryAcquire方法的区别了,两个不同点都是,非公平锁都是想强行获取资源,而公平锁则比较好,排队。

2.那么非公平锁有什么缺点呢?显而易见,可能导致后面排队等待的线程等不到相应的cpu资源,从而引起线程饥饿。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值