ReentrantLock原理以及简单手写ReentrantLock

本文介绍了ReentrantLock的原理,它是基于AQS、CAS和LockSupport实现的可重入锁,支持公平和非公平模式。通过手写一个简单的ReentrantLock类,阐述了加锁、释放锁的流程,以及主要类和方法的设计,帮助读者深入理解ReentrantLock的工作机制。

ReentrantLock原理以及简单手写ReentrantLock

最近在以ReentrantLock为例研究aqs的源码实现,以下介绍一下自己的心得,以及简单手写一个ReentrantLock锁类,加深对ReentrantLock的原理了解。

ReentrantLock原理

ReentrantLock是java中最常见的锁,ReentrantLock是利用AQS队列+CAS+
LockSupport来实现的,它是一种独占锁、可重入锁,它支持公平锁和非公平锁模式。
AbstractQueuedSynchronizer(AQS),类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。
了解AQS实现原理有助于我们理解其他并发工具类。本文不注重探究ReentrantLock源码实现原理(下篇文章再来探究源码),以手写锁类来揭示
ReentrantLock实现思想,尽量保持方法名与源码方法一致,方便同学们对照源码学习,本文只是简单实现,源码比较复杂的设计就一一略过了,有兴趣的同学可以参照源码研究。

简单手写ReentrantLock

package com.argus.aqs.aqsdemo;

import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

/**
 * @description: 手写reentrantlock
 * @author: argus
 * @date: Created in 2023/3/15 15:46
 * @version: 1.0.0
 * @modified By:
 */
public class ArgusLock {

    private final ArgusSync sync;

    public ArgusLock(){
        sync = new nonfailArgusSync();
    }
    public ArgusLock(boolean fail){
        sync= fail ? new failArgusSync() : new nonfailArgusSync();
    }

    public void lock(){
        sync.lock();
    }

    public void unlock(){
        sync.release(1);
    }


    static abstract class ArgusSync{
        //cas 控制锁状态  0没有线程占用  1 被线程专用
        private AtomicInteger atomicInteger = new AtomicInteger(0);
        //没有获取锁的线程存放队列
        private ConcurrentLinkedDeque<Thread> concurrentLinkedDeque = new ConcurrentLinkedDeque<>();
        //当前锁持有线程
        private transient Thread exclusiveOwnerThread;

        private transient Thread headThread;

        protected void setExclusiveOwnerThread(Thread thread){
            exclusiveOwnerThread = thread;
        }

        protected Thread getExclusiveOwnerThread(){
            return exclusiveOwnerThread;
        }

        protected int getAtomicInteger(){
            return atomicInteger.get();
        }

        protected boolean dequeIsEmpty(){
            return concurrentLinkedDeque.isEmpty();
        }

        protected Thread getHeadThread(){
            return headThread;
        }

        protected void setHeadThread(Thread thread){
            headThread = thread;
        }

        /**
         * cas获取锁的状态
         * @param expect
         * @param update
         * @return
         */
        protected boolean compareAndSet(int expect, int update){
            return atomicInteger.compareAndSet(expect,update);
        }

        /**
         * 加锁
         */
        abstract void lock();

        /**
         * 释放锁
         */
        public void release(int releases){
            if(tryRelease(releases)){
                //释放锁成功  将等待线程唤醒
                if(!concurrentLinkedDeque.isEmpty()){
                    //将头部元素唤醒 非公平锁可以检索所有等待线程 全部唤醒重新争抢资源 这里按照
                    //ReentrantLock方式非公平锁直接竞争资源  加入等待队列后  排队竞争
                    Thread thread = concurrentLinkedDeque.pollFirst();
                    if(null != thread){
                        //设置头部节点  公平锁自旋判断需要
                        setHeadThread(thread);
                        LockSupport.unpark(thread);
                    }
                }
            }
        }

        /**
         * 每次释放锁 将state减1 直至等于0  代表锁不被占有
         * @param releases
         * @return
         */
        public boolean tryRelease(int releases){
            int state = getAtomicInteger();
            int c = state - releases;
            boolean free = false;
            //必须持有锁线程才能释放锁
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            if(c == 0){
                //释放锁成功
                free = true;
                setExclusiveOwnerThread(null);
            }
            if (c < 0) {
                throw new Error("Maximum lock count exceeded");
            }
            //修改原子类的值  并不是直接改为0  而是每次减1  重入需要多次释放锁
            compareAndSet(state,c);
            return free;
        }

        /**
         * 获取锁
         * @param arg
         */
       protected void acquire(int arg){
            if(!tryAcquire(arg)){
                //再次尝试获取锁失败或者也不是线程重入  加入等待队列并自旋 线程阻塞
                acquireQueued(arg);
            }
       }

        /**
         * 尝试获取锁
         * @param arg
         * @return
         */
       abstract boolean tryAcquire(int arg);

        /**
         * 线程获取锁失败 自旋并加入队列  阻塞
         * @param arg
         */
       private void acquireQueued(int arg){
         try {
             boolean failed = true;
             for (;;){
                 //防止并发情况下  新入线程抢先获取到锁
                 if(tryAcquire(arg)){
                     //获取到锁  直接跳出循环
                     failed = false;
                     return;
                 }
                 //获取失败加入等待队列
                 concurrentLinkedDeque.add(Thread.currentThread());
                 //阻塞线程
                 LockSupport.park();
             }
         }finally {
             //增加保险策略  如果有意外情况将该线程移除
            if(false){
                cancleAcquire(Thread.currentThread());
            }
         }
       }

        /**
         * 将线程从异常中移除
         * @param thread
         */
       private void cancleAcquire(Thread thread){
           concurrentLinkedDeque.remove(thread);
       }

    }

    static class failArgusSync extends ArgusSync{

        @Override
        void lock() {
            acquire(1);
        }

        @Override
        boolean tryAcquire(int arg) {
            Thread thread = Thread.currentThread();
            int state = getAtomicInteger();
            //锁没有被占有
            if(state == 0){
                //判断队列是否有元素 再次尝试获取锁
                //俩种情况  第一次获取锁 直接判断是否有等待线程  第二种  判断是否是唤醒的线程
                if((dequeIsEmpty() || thread == getHeadThread()) && compareAndSet(0,1)){
                    setExclusiveOwnerThread(thread);
                    //删除头部节点
                    setHeadThread(null);
                    return true;
                }
            }else if(thread == getExclusiveOwnerThread()){
                //添加锁重入机制
                int newState = state+arg;
                //加上保护措施
                if (newState < 0) {
                    throw new Error("Maximum lock count exceeded");
                }
                //将锁次数加1
                compareAndSet(state,newState);
                return true;
            }
            return false;
        }
    }

    static class nonfailArgusSync extends ArgusSync{
        @Override
        void lock() {
            //直接获取锁
            if(compareAndSet(0,1)){
                //获取到锁 将当前线程设置为持有锁的线程
                setExclusiveOwnerThread(Thread.currentThread());
            }else{
                acquire(1);
            }
        }

        @Override
        boolean tryAcquire(int arg) {
            Thread thread = Thread.currentThread();
            int state = getAtomicInteger();
            //锁没有被占有
            if(state == 0 ){
                //再次尝试获取锁
                if(compareAndSet(0,1)){
                    setExclusiveOwnerThread(thread);
                    return true;

                }
            }else if(thread == getExclusiveOwnerThread()){
                //添加锁重入机制
                int newState = state+arg;
                //加上保护措施
                if (newState < 0) {
                    throw new Error("Maximum lock count exceeded");
                }
                //将锁次数加1
                compareAndSet(state,newState);
                return true;
            }
            return false;
        }
    }
}

主要类和方法

ArgusLock:自定义锁类 里面定义了静态内部类ArgusSync以及加锁lock()、释放锁unlock()的方法。提供俩个构造器,一个无参构造,默认是非公平锁,一个有参构造 当参数为true是公平锁,false非公平锁,与ReentrantLock用法一样。
在这里插入图片描述

ArgusSync:静态抽象内部类,相当于ReentrantLock的Sync类,Sync类是通过继承AbstractQueuedSynchronizer,通过Unsafe提供的硬件级别的原子操作来实现CAS操作,这里直接通过AtomicInteger原子类来实现,AQS内部维护了一个虚拟的双向链表FIFO队列,每一个等待的线程构成一个Node节点,这里直接用并发包下的concurrentLinkedDeque替代。
atomicInteger: cas 控制锁状态 0没有线程占用 1 被线程专用
concurrentLinkedDeque:没有获取锁的线程存放队列
exclusiveOwnerThread:当前锁持有线程
headThread:头部节点(AQS中的双端链表的头结点是一个无参构造函数的头结点,头部节点的后置节点才是等待唤醒的线程节点,这里方便后续线程自旋唤醒判断逻辑,保留头部节点)
在这里插入图片描述

failArgusSync:公平锁类 继承ArgusSync
nonfailArgusSync:非公平锁类 继承ArgusSync

加锁、释放锁流程

加锁:ArgusSync.lock(),里面调用了ArgusSync的lock()
在这里插入图片描述
公平锁实现:调用ArgusSync.acquire(),在这里插入图片描述
可以看到先调用tryAcquire(),初次获取锁状态(非公平、公平锁实现不同),如果获取失败加入等待队列,acquireQueued()。
在这里插入图片描述
公平锁获取锁,先判断锁状态,如果为0 代表没有线程占有锁,下面有俩种情况等待线程为空,或者当前线程为头结点(自旋、阻塞被唤醒之后),直接获取锁状态compareAndSet(),如果成功代表当前线程获取到所锁,将持有线程设为当前线程,同时将头部节点标识删除(针对第二种情况)。如果锁被占有时要判断是否是线程重入(锁的重入性逻辑),如果持有线程是当前线程代表锁重入,不需要再次获取锁,直接将当前锁状态state加1。

在这里插入图片描述
当线程没有获取到锁时,加入等待列,通过自旋一直获取锁,获取到锁直接跳出自旋,没有获取到锁,加入等到队列末尾,防止自旋次数导致浪费cpu资源,并将线程阻塞。cancleAcquire()防止意外情况下将线程移除等待队列(在ReentrantLock设计中,cancleAcquire方法是将当前node的waitstatus设为-1即取消状态,感兴趣的同学可以去研究下源码,本人在研究的时候发现cancleAcquire方法好像不会触发,不知道是不是作者的一种保险策略)。

非公平锁:与公平锁大致相同
在这里插入图片描述
在加锁的时候直接先获取锁的状态,获取失败在调用acquire()
在这里插入图片描述
非公平锁的tryAcquire(),先是直接获取一次锁状态,像其他重入逻辑类似

释放锁

非公平锁、公平锁释放锁的原理一致
在这里插入图片描述
必须锁持有线程才能释放锁,获取锁的state每次减1(不是直接修改为0,因为重入需要多次释放锁),释放锁成功后清除当前持有线程。

在这里插入图片描述
释放锁成功后如果等待队列中有等待线程,直接获取头部线程(pollFirst检索双端队列的头部元素并删除,不清楚的同学可以看下ConcurrentLinkedDeque的相关api),并将线程unpark(),加入自旋获取锁状态。

总结

简单手写ReentrantLock实现原理只是为了加深自己在研究源码时的理解,参考了ReentrantLock源码 但是设计也比较粗糙,比如AQS是怎么维护双端队列的node节点、删除状态为CANCELLED的节点、shouldParkAfterFailedAcquire方法的实现、addWaiter添加末尾节点、cancelAcquire等都只是简单化处理。感兴趣的同学可以自己去研究下源码,这里只是提供了一种简便思想方便大家理解ReentrantLock源码。理解有限,大佬勿喷。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值