1.8ReentrantLock史上最详细源码分析

2019-11-02  本文已影响0人  IT界刘德华

前言

ReentrantLock是基于AQS实现的可重入的独占锁,当然它可以实现公平锁和非公平锁,下面会一一进行讲解。这里一些AQS的知识就不过多的介绍,详细见我的AQS篇章https://www.jianshu.com/p/54d372425e54

类结构

public class ReentrantLock implements Lock, java.io.Serializable {
    //内部类持有对象,是公平锁还是非公平锁,该对象继承了AbstractQueuedSynchronizer 
    private final Sync sync;

    //默认初始化是非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    //传参true知道公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    //继承AQS框架
    abstract static class Sync extends AbstractQueuedSynchronizer {

    }

    //非公平锁
    static final class NonfairSync extends Sync {}

    //公平锁
    static final class FairSync extends Sync {}
}

源码分析

NonfairSync(非公平锁分析)
final void lock() {
            //状态是0代表可以获取锁,如果内存中是0就获取锁,并cas改成1表示得到锁重入次数是1
            if (compareAndSetState(0, 1))
                //得到锁则将当前线程往爷爷类AbstractOwnableSynchronizer设置为拥有独占锁的线程
                setExclusiveOwnerThread(Thread.currentThread());
            //没获得锁
            else
                //请求获得资源
                acquire(1);
        }
protected final boolean compareAndSetState(int expect, int update) {
        // AbstractQueuedSynchronizer对象+状态偏移量找到内存的状态值
        //如果内存的状态值和expect期望值相等,则用update更新内存的状态值
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
//获取资源,如果tryAcquire没获取到资源,则acquireQueued从等待队列自旋去获取资源
//如果依然没获取到则进入阻塞状态,当然如果acquireQueued在判断取到的线程已经被中断
//则调用selfInterrupt去打断线程
//参数arg,该值会传送到{@link #tryAcquire},但否则不会被解释,可以代表任何内容,如reentrantLock传的是1。
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
//在sync内部的方法
//非公平锁获取资源
final boolean nonfairTryAcquire(int acquires) {
            //当前线程
            final Thread current = Thread.currentThread();
            //获取父类AbstractQueuedSynchronizer的计数值
            int c = getState();
            //锁还未被获取
            if (c == 0) {
                //acquires=1传进来的,cas设置state的状态,改为已经被当前线程获得锁,并标记重入次数
                if (compareAndSetState(0, acquires)) {
                    //设置当前线程为当前锁的独占线程
                    setExclusiveOwnerThread(current);
                    //获得锁成功
                    return true;
                }
            }
            //状态!=0s说明已经被获得锁
            //如果获得锁的独占线程是当前线程则可以继续获得锁
            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;
        }
FairSync(公平锁分析)
final void lock() {
            acquire(1);
        }
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//尝试获取公平锁
protected final boolean tryAcquire(int acquires) {
            //当前线程
            final Thread current = Thread.currentThread();
            //锁的重入次数
            int c = getState();
            //锁还未被获取
            if (c == 0) {
                //hasQueuedPredecessors判断队列是否还有等待时间长的线程,也就是排在队列前的
                //如果没有则比较设置重入值,获锁成功设置当前线程为锁的独占线程返回true
                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;
        }
//查询是否有任何线程等待获取的时间比当前线程长
//如果h==t成立,h和t均为null或是同一个具体的节点,无后继节点,返回false。
//如果h!=t成立,head.next是否为null,如果为null,返回true。什么情况下h!=t的同时h.next==null??,
//有其他线程第一次正在入队时,可能会出现。见AQS的enq方法,compareAndSetHead(node)完成,还没执行tail=head语句时,
//此时tail=null,head=newNode,head.next=null。
//如果h!=t成立,head.next != null,则判断head.next是否是当前线程,如果是返回false,
//否则返回true(head节点是获取到锁的节点,但是任意时刻head节点可能占用着锁,也可能释放了锁(unlock()),
//未被阻塞的head.next节点对应的线程在任意时刻都是有必要去尝试获取锁)
public final boolean hasQueuedPredecessors() {
      
        Node t = tail; 
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
tryLock(其实调用的就是Sync类的nonfairTryAcquire非公平获取资源方法)

没获取到锁就返回了不会跟lock一样还要一直阻塞

public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
unlock(解锁)

调用的是AQS类的release方法

public void unlock() {
        sync.release(1);
    }
//释放资源
public final boolean release(int arg) {
    //将重入次数减到0后释放锁成功
    if (tryRelease(arg)) {
        //头结点
        Node h = head;
        //头结点不是空并且它的状态不是初始化阶段,如果是<0的状态会将当前节点置为0
        //并且找到有效的后继节点进行释放
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        //释放成功
        return true;
    }
    //释放失败
    return false;
}
//尝试释放锁,releases释放次数,因为锁是可重入的,上了几次锁就要解锁几次
        protected final boolean tryRelease(int releases) {
            //减掉释放的锁就是还剩锁次数
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //已经完全解锁
            if (c == 0) {
                free = true;
                //清空独占线程
                setExclusiveOwnerThread(null);
            }
            //设置新的锁次数
            setState(c);
            //返回释放状态
            return free;
        }

有些方法没去讲,因为是调用AQS的方法,已经说过就不讲了
AQS详见我的AQS源码解析篇https://www.jianshu.com/p/54d372425e54
觉得还可以的话给我点点赞呀!!!!!

上一篇下一篇

猜你喜欢

热点阅读