ReentrantLock学习(三)公平锁与非公平锁

一、概念理解

公平锁:申请所的时候排队,谁也不插队

非公平锁:申请的时候插队(先插队,不行了再排队)

 

二、差别

ReentrantLock的公平锁与非公平锁的差别在于,内部的同步器不一样,lock()方法调用的是sync的lock(),分别是FairSync与NonfairSync。

sync的lock()内部调用了AQS的acquire,而AQS的acquire做了三件事,第一件是直接申请锁(tryAcquire),第二件是没申请下锁的话,就创建线程等待节点并添加到等待队列(addWaiter),第三件是将改节点的prev节点标记为SIGNAL,并挂起该线程,等待唤醒再申请锁(acquireQueued)。

FairSync与NonfairSync的区别在tryAcquire方法不同,如下代码及注释(文末有lock流程图,一眼看差别

公平锁同步器

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
        
    final void lock() {
        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();
        //状态等于0,锁没有占用
        if (c == 0) {
            //当前队列无等待线程 
            if (!hasQueuedPredecessors() &&
                //修改锁状态成功
                compareAndSetState(0, acquires)) {
                //设置当前占着锁的线程为当前线程
                setExclusiveOwnerThread(current);
                //获取锁成功
                return true;
            }
        }
        //状态不为0,锁被占用,占用锁的线程是当前线程(重入)
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded”);
            //叠加状态(状态为记录当前线程占用锁的次数)
            setState(nextc);
            //获取锁成功
            return true;
        }
        //获取锁失败
        return false;
    }
}

非公平锁同步器

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        //直接修改锁状态为1,也就是先尝试申请一下看看能否成功
        if (compareAndSetState(0, 1))
            //设置当前占着锁的线程为当前线程,申请锁成功
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //acquire方法会调用tryAcquire
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //状态等于0,锁没有占用
    if (c == 0) {
        //修改锁状态为1
        if (compareAndSetState(0, acquires)) {
            //设置当前占着锁的线程为当前线程,申请锁成功
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //状态不为0,锁被占用,占用锁的线程是当前线程(重入)
    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;
}

ps:hasQueuedPredecessors方法

((s = h.next) == null || s.thread != Thread.currentThread())  是为了过滤掉两种临界情况

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;
    //h != t 这是必要条件
    return h != t &&
        //h.next == null 说明head存在,tail还未设置,该种情况出现在addWaiter添加第一个等待节点时出现,设置完head还未来得及设置tail
        //s.thread != Thread.currentThread() 该情况是防止,当前线程刚被unpark,首次执行tryAcquire时,head还未替换成当前线程节点
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

 

三、 白话总结

  1. 公平锁在申请的时候先看有线程在排队没,有的话就去排队,没有的话就申请锁(所有申请锁线程都满足FIFO)。
  2. 非公平锁在申请的时候,先插队看看(也就是直接申请锁),可以的话就申请成功,不行的话再去排队,排上队的线程获取锁顺序就不会变了(排上队的线程满足FIFO)。

附件: 

非公平锁 lock流程图

非公平锁lock流程图
非公平锁lock流程图

 

公平锁 lock流程图

公平锁lock流程图
公平锁lock流程图

 

 

unlock流程

unlock流程图
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值