ReentrantLock内部原理实现

ReentrantLock是基于AQS(AbstractQueuedSynchronizer)来进行实现的,分为公平锁和非公平锁,可以通过在构造器中出入true将ReentrantLock设置为一个公平锁。

AQS是一个FIFO的双向队列,自内部通过节点head、tail记录队首和队尾元素,队列元素的类型为Node,其中Node中thread变量用来存放进入AQS队列里面的线程,Node节点内部的SHARED用来标记该线程是获取共享资源而被阻塞挂起后放入AQS队列中的,EXCLUSIVE用来标记线程是获取独占资源时被官气后放入AQS队列中的,waitStatus记录当前线程等待状态,可以为CANCELLED(线程被取消了)、SIGNAL(线程需要被唤醒)、CONDITION(线程在条件队列里面等待)、PROPAGATE(释放共享资源时需要通知其他节点);prev记录当前节点的前驱节点,next记录当前节点的后继节点。

对于ReentrantLock来说,AQS的state 用来记录当前线程获取锁的可重入次数,不同工具类有不同的含义,例如读写锁ReentrantReadWriteLock 将state的高16位表示读状态,也就是获取该锁的读的次数,低16位表示获取到写锁的线程的可重入次数

AQS也是主要提供了4个方法,分别为:tryAcquire、tryAcquireShared、tryRelease、tryReleaseShared 分别供子类实现

下面主要讲下 ReentrantLock主要逻辑:

非公平锁

1、new ReentrantLock() 创建一个非公平锁,内部实现是创建了一个名称为sync的内部类NonfairSync

    public ReentrantLock(boolean fair) {
    	//fair为true表示公平锁,false为表示非公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、调用ReentrantLock的lock方法,内部调用sync的lock方法,方法语义为:将AQS的state由0通过cas更改为1

    public void lock() {
        sync.lock();
    }
final void lock() {
			//尝试cas更新state状态
            if (compareAndSetState(0, 1))
                ...
            else
                ....
        }

3、第二步成功的话,将AQS中的独占线程exclusiveOwnerThread设置为自己

final void lock() {
            if (compareAndSetState(0, 1))
            	//如下代码,将AQS中的独占线程设置为自己
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

4、第二步失败的话,调用AQS的acquire方法即独占锁获取方法,获取一个资源

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            	//如下代码,调用AQS的acquire方法
                acquire(1);
        }

5、继续调用ReentrantLock中nonfairTryAcquire方法即非公平锁获取实现

    public final void acquire(int arg) {
    	//如下代码,此处tryAcquire为 ReentrantLock的NonfairSync非公平模式的方法实现
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

6、方法内部语义为:获取state状态,如果等于0 则尝试更改为1,并设置独占线程标识

        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()) {
                ...
            }
            return false;
        }

7、否则在判断独占锁标识是否为自己,如果是 则重入并将state增1

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

8、都不成功则进行接下来的操作

9、新增节点,将当前线程为参数创建新节点,加入到链表尾部

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
        	//如下代码 中的 addWaiter方法
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

10、调用AQS的acquireQueued方法

   public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
        	//如下代码 中的 acquireQueued方法
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

11、在方法内部进行循环,通过新加进来的节点获取前一个节点

    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)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

12、如果前一个节点是头节点,则尝试调用tryAcquire方法获取锁,获取成功后将自己设置为头结点,并移除节点内部关联的线程属性

if (p == head && tryAcquire(arg)) {
 	setHead(node);
    p.next = null; // help GC
    failed = false;
    return interrupted;
}

13、如果不是头节点或者获取锁失败的话,则尝试修改节点状态waitStatus 并 将自己的线程阻塞

//方法:shouldParkAfterFailedAcquire修改状态、parkAndCheckInterrupt阻塞
 if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;

14、因为是循环,最后将所有标志为取消状态的节点移除,将其他状态的修改为等待状态。但是 由13步可知,每个后继节点只负责将自己的前驱节点修改状态,因为 修改成功后 自己线程就被阻塞了,等待唤醒。

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

公平锁

对于公平锁来说,其与非公平锁的区别在于 公平锁没有第三步,直接就是第四步 acquire方法调用,并且 acquire方法中 的 tryAcquire方法实现是公平锁的实现方式,

        protected final boolean tryAcquire(int acquires) {
        	//获取当前线程
            final Thread current = Thread.currentThread();
            //获取AQS中的state状态值
            int c = getState();
            //如果等于0
            if (c == 0) {
            	//通过 hasQueuedPredecessors方法 判断 阻塞队列 head头节点的下一个节点是否为当前线程的节点
                if (!hasQueuedPredecessors() &&
                	//尝试修改state的状态为1
                    compareAndSetState(0, acquires)) {
                    //以上两个条件都满足的话 则将AQS中的独占线程 设置为自己
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //否则通过getExclusiveOwnerThread方法获取AQS中的独占线程,并判断是否为自己当前线程
            else if (current == getExclusiveOwnerThread()) {
            	//是的话,将 state + 1
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                //将state的值刷新到主内存中
                setState(nextc);
                return true;
            }
            return false;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值