Java并发Concurrent包的锁(三)——ReentrantLock源码分析

ReentrantLock 提供了两个构造函数,默认是非公平锁的构造函数,还有一个公平锁的构造函数。
可以参考: Java并发Concurrent包的锁(二)——自旋/阻塞/可重入

ReentrantLock 类中的公平和非公平,是通过对同步器 AbstractQueuedSynchronizer 的扩展加以实现的,也就是在tryAcquire的实现上做了语义的控制。

    // 默认的非公平可重入锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    // 公平可重入锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

下面通过关键源码来分析:
非公平锁:

        final boolean nonfairTryAcquire(int acquires) {
           // 当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果当前状态c==0,说明是初始状态,将当前锁owner设置为当前线程,state加acquires的值一般为1
            if (c == 0) {
                // 尝试将state的值从0设置为acquires的值,一般为1
                if (compareAndSetState(0, acquires)) {
                    // 将当前锁owner设置为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果锁状态已经被设置,而且锁的owner就是当前线程,那么就是可重入锁
            else if (current == getExclusiveOwnerThread()) {
                // 那么将之前的计数状态加上acquires的值
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                // 更新state的计数值
                setState(nextc);
                return true;
            }
            // 如果是其他线程控制的锁,返回false说明当前线程无法获得锁
            return false;
        }

公平锁:

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果当前状态c==0,说明是初始状态,将当前锁owner设置为当前线程,state加acquires的值一般为1
            if (c == 0) {
                // 这个hasQueuedPredecessors下面分析
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    // 将当前锁owner设置为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果锁状态已经被设置,而且锁的owner就是当前线程,那么就是可重入锁
            else if (current == getExclusiveOwnerThread()) {
                // 那么将之前的计数状态加上acquires的值
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                // 更新state的计数值
                setState(nextc);
                return true;
            }
            // 如果是其他线程控制的锁,返回false说明当前线程无法获得锁
            return false;
        }

公平锁中有 hasQueuedPredecessors 方法,加入了当前线程(Node)之前是否有前置节点在等待的判断。
公平锁中有一个等待队列,遵循 FIFO 原则:

    public final boolean hasQueuedPredecessors() {
        // 尾节点
        Node t = tail;
        // 头节点 
        Node h = head;
        Node s;
        // 链表中有其他节点,并且头结点后第一个等待的线程是当前线程时,返回true
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

释放锁:

        protected final boolean tryRelease(int releases) {
            // 状态值减去releases值
            int c = getState() - releases;
            // 锁对象的owner必须是当前对象,当前对象才能释放锁
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 状态值为0
            if (c == 0) {
                // free置为true代表释放锁成功
                free = true;
                // 锁对象的owner置为null,锁完全释放
                setExclusiveOwnerThread(null);
            }
            // 更新锁的状态值
            setState(c);
            return free;
        }

公平锁在队列中取线程,保证先到先得,确保了公平性,但是吞吐量就没有非公平锁高了。非公平锁,由于可以在快速获取机制下获取下一个线程,保证进入和退出锁的吞吐量,但是队列中过早排队的线程会一直处于阻塞状态,造成“饥饿”场景。
在实际中,还要看线程临界区执行的时长大小,如果临界区时间比较长,那么相比之下公平锁带来的负担就相对小一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值