ReentrantLock 源码学习

并发包下面的源码分析帖子早就烂大街了,但别人总结的永远不是自己理解的,不会的时候再去看别人总结好的东西,下面是整理的学习ReentrantLock的lock。

首先看下ReentrantLock的构造器

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

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

 ReentrantLock其实是分为公平锁和非公平锁,默认的是非公平锁,NonfairSync和FairSync都继承了   AbstractQueuedSynchronizer(下面简称AQS)队列同步器,具体这两种锁的表现最后会总结。

先看下非公平锁的源码

final void lock() {
在AQS中有个重要的states,如果这个states是0的话证明锁是释放状态,这块乐观锁尝试states加到1
如果成功那么将当前线程设置为锁的拥有者
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
如果没成功那么将这个线程放到队列
        acquire(1);
}

乐观锁尝试states加到1

protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

现在重点看下AQS中的acquire方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        如果当前线程没有获取锁,addWaiter设置Node为独占模式,轮询唤醒队列中的当前线程,返回中断状态
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
tryAcquire方法的实现在具体的子类中,这个方法还是用当前线程去尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
如果现在states是0的话那么再次做尝试获取锁的尝试(同lock方法)
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
如果当前线程就是锁的拥有者state增加(说白了就是支持重入,毕竟重入锁)
    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;
}


在AQS中分为两种模式,一种是独占EXCLUSIVE,一种是共享SHARED
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

再重点看下AQS中的acquireQueued方法

此方法干的事就是轮询,等前面的资源唤醒自己(返回的事自己是否被中断过,false未被中断过)

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        是否被中断过
        boolean interrupted = false;
        轮询
        for (;;) {
            当前节点的上一个节点
            final Node p = node.predecessor();
            如果上一个节点是头并且tryAcquire,熟悉不?上面有啊,当前线程尝试获取锁
            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);
    }
}

检查当前节点是否进入了wait状态
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

private final boolean parkAndCheckInterrupt() {
            使线程进入waiting状态
            LockSupport.park(this);    
            你调用interrupted方法,第一次会返回true。
            然后,当前线程的中断状态被方法内部清除了。
            第二次调用时就会返回false            
            interrupted是静态方法,返回的是当前线程的中断状态
            第一次使用该方法如果已经中断,
            第二次使用该方法则会取消中断。
            调用interrupted方法,第一次会返回true。
            然后,当前线程的中断状态被方法内部清除了。
            第二次调用时就会返回false。
            return Thread.interrupted();
        }

看公平锁对比非公平锁来看

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

可以看到公平锁不会有去让当前线程去再次尝试拿到锁的动作,直接acquire,进去acquire方法中和非公平锁唯一的不同就是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;
}

总结:非公平锁是当线程来了先拿当前线程和队列中的头进行竞争拿锁,而公平锁直接是将线程加入到队尾等待前面的节点唤醒, 从效率上来讲非公平锁更高效,减少了唤醒等待和线程切换,不过这也可能有造成队列中的线程等待时间较长的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值