ReentrantLock源码学习
ReentrantLock源码学习
zengjiaqwe 发表于3个月前
ReentrantLock源码学习
  • 发表于 3个月前
  • 阅读 0
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

摘要: jdk可重入锁源码学习

ReentrantLock实际是通过AQS同步器来实现的,AQS本质是一个FIFO队列,利用volatile修饰的status维护状态,基于先进先出的队列管理多线程请求。通过Unsafe类compareAndSwap方法维护status状态的修改,这是一个natvie方法,使用硬件层面的并发;

ReentrantLock构造方法

默认是非公平锁

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

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

一、非公平锁

1.NonfairSync类 lock()

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

利用CAS将同步器状态从0改为1成功,则将当前排他锁的持有者设置为当前线程;

否则,再来看acquire()方法,acquire是父类AQS的方法

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

    先尝试获取锁,调用tryAcquire(),NonfairSync的tryAcquire()实际调用nonfairTryAcquire()

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

    如果同步器状态为0,利用CAS修改同步器状态,修改成功,设置同步器的持有线程为当前线程;如果同步器持有的线程已经是当前线程,则将同步器的状态值累加。(一个线程可以多次lock);

    如果尝试获取锁失败,回到acquire方法;先构造一个Node,看AQS中的addWaiter方法实现

    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)) {
                // 队列已经初始化,在尾部增加node结点
                pred.next = node;
                return node;
            }
        }
        // 自旋,队列未初始化或由于其它线程竞争导致队列尾部添加结点失败时执行enq(),把node结点放入尾部
        enq(node);
        return node;
    }

    第一个构建Node的线程,会先进入到enq方法,enq方法具体实现如下:

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

        初始化流程,创建一个新Node节点t,并且设置为头部节点

        此时head和tail都指向了新构造的t这个Node

        再将node节点,设置到队列尾部

        

    acquireQueued()

    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)) {
                    // 发现node的前驱是头结点并且node获取状态成功,则释放头结点
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 进入阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

PS:如果当前线程节点的前驱节点不是头部节点,会进入阻塞状态,进入阻塞时会将前驱节点状态设置为signal,当前驱节点对应的线程释放锁时,会根据signal唤醒下一个节点对应的线程。阻塞是利用Unsafe中的park(),native方法。

 

 

 

 

 

2.AQS的release()

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                // 锁释放成功,则唤醒后继节点线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

 

二、公平锁

// FairSync lock 公平锁lock
final void lock() {
    acquire(1); // AQS类方法
}

// AQS类方法
public final void acquire(int arg) {
    // 对比非公平锁,没有走快捷方法直接拿到锁,会先判定等待队列中是否有其它线程节点,有的话会直接进入等待队列
    // 会比非公平锁有更多的线程上下文切换,高并发场景下,性能不如非公平锁
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

// FairSync tryAcquire
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 确认当前没有线程拿到锁,判断等待队列中是否有线程节点等待,如果没有,当前线程尝试更改AQS状态
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 同一个线程多次lock操作
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

 

共有 人打赏支持
粉丝 0
博文 1
码字总数 1015
×
zengjiaqwe
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: