Java利用AQS实现自定义锁

 更新时间:2022年07月25日 15:34:38   作者:#小苏打  
本文主要介绍了Java利用AQS实现自定义锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

什么是AQS

AQS(AbstractQueuedSynchronizer),中文名抽象队列同步器

AQS定义了一套多线程访问共享资源的同步器框架,主要用来自定义锁和同步器

AQS原理

AQS 核心思想:

  • 如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。
  • 如果被请求的共享资源被占用,将暂时获取不到锁的线程加入到阻塞队列中,等待被唤醒和锁的分配

实现核心思想的的队列:CLH队列

CLH队列是一个虚拟的双向队列,AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

共享资源用 volatile 关键词修饰,保证线程间的可见性

   /**
     * The synchronization state.
     */
    private volatile int state;

0状态表示空闲,1状态或以上表示不空闲

共享资源(state)的访问方式有三种:  

  1. getState()   获得共享资源状态
  2. setState()   设置共享资源状态
  3. compareAndSetState() 更改共享资源状态(底层unsafe类)

 源代码如下

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }
 
    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }
    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

利用AQS实现自定义锁

一:首先创建一个类实现Lock接口,它有6个方法需要实现

  • lock():加锁(不成功进入阻塞队列等待)
  • lockInterruptibly():是否加锁可打断
  • tryLock()://加锁(不成功不会进入阻塞队列等待,可以去做其他事情)
  • tryLock(long time,TimeUnit unit):加锁(规定时间内未获得则放弃加锁)
  • unlock():释放锁
  • newCondition():创建条件变量

二:创建一个内部类,继承AbstractQueuedSynchronizer

可以根据需求重写具体方法,总共有5种方法

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

三:我需要自定义一个独占锁不可重入具有变量条件的锁

分析

  • 独占锁:AQS同步器中需要重写独占方式的获取资源tryAcquire(int)和释放资源tryRelease(int)方法
  • 不可重入:AQS同步器需要实现isHeldExclusively():
  • 具有条件变量:AQS同步器中 return new ConditionObject();

具体代码如下

//自定义锁(不可重入)(独占锁)(条件变量)
class MyLock implements Lock{
    //内部类,AQS同步器类
    class MySync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0,1)){
                System.out.println("获得锁成功");
                //加上了锁,并设置owner为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            System.out.println("获得锁失败");
            return false;
        }
 
        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
 
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
 
        public Condition newCondition(){
            return new ConditionObject();
        }
    }
 
    private MySync mySync = new MySync();
 
    @Override //加锁(不成功进入阻塞队列等待)
    public void lock() {
        mySync.acquire(1);
    }
 
    @Override //加锁可打断
    public void lockInterruptibly() throws InterruptedException {
        mySync.acquireInterruptibly(1);
    }
 
    @Override //加锁(不成功不会进入阻塞队列等待,可以去做其他事情)
    public boolean tryLock() {
        return mySync.tryAcquire(1);
    }
 
    @Override //尝试加锁 带时间
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return mySync.tryAcquireNanos(1,unit.toNanos(time));
    }
 
    @Override //释放锁
    public void unlock() {
        mySync.release(1);
    }
 
    @Override //创建条件变量
    public Condition newCondition() {
        return mySync.newCondition();
    }
}

到此这篇关于Java利用AQS实现自定义锁的文章就介绍到这了,更多相关Java AQS实现自定义锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • dubbo泛化调用使用及原理示例解析

    dubbo泛化调用使用及原理示例解析

    这篇文章主要为大家介绍了dubbo泛化调用使用及原理示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Java中使用增强for循环的实例方法

    Java中使用增强for循环的实例方法

    在本篇文章里小编给大家整理是的关于Java中如何使用增强for循环的实例内容以及相关代码,需要的朋友们可以学习下。
    2019-08-08
  • Mybatis给数据库敏感字段加解密详解

    Mybatis给数据库敏感字段加解密详解

    这篇文章主要介绍了Mybatis给数据库敏感字段加解密详解,为了保护数据库敏感字段数据安全,有时候我们需要将敏感数据加密入库,查询时再解密成明文,我们可以利用Mybatis自定义TypeHandler来处理,需要的朋友可以参考下
    2023-11-11
  • Java中的ArrayList容量及扩容方式

    Java中的ArrayList容量及扩容方式

    这篇文章主要介绍了Java中的ArrayList容量及扩容方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Springboot如何使用Map将错误提示输出到页面

    Springboot如何使用Map将错误提示输出到页面

    这篇文章主要介绍了Springboot如何使用Map将错误提示输出到页面,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Redis Lettuce连接redis集群实现过程详细讲解

    Redis Lettuce连接redis集群实现过程详细讲解

    这篇文章主要介绍了Redis Lettuce连接redis集群实现过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-01-01
  • spring boot配置dubbo方式(properties)

    spring boot配置dubbo方式(properties)

    这篇文章主要介绍了spring boot配置dubbo方式(properties),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • Java基础之匿名内部类、包装类

    Java基础之匿名内部类、包装类

    这篇文章主要给大家介绍了关于Java中方法使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • Springboot整合MongoDB的Docker开发教程全解

    Springboot整合MongoDB的Docker开发教程全解

    这篇文章主要介绍了Springboot整合MongoDB的Docker开发,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2020-07-07
  • 深入理解Java new String()方法

    深入理解Java new String()方法

    今天给大家带来的是关于Java的相关知识,文章围绕着Java new String()展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06

最新评论