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实现自定义锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java线程状态变换过程代码解析

    Java线程状态变换过程代码解析

    这篇文章主要介绍了Java线程状态变换过程代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • 深入理解Spring AOP

    深入理解Spring AOP

    这篇文章主要介绍了深入理解Spring AOP,详细的介绍了spring aop的具体实现与理论
    2017-01-01
  • Java Swing JList列表框的实现

    Java Swing JList列表框的实现

    这篇文章主要介绍了Java Swing JList列表框的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • IntelliJ IDEA编译项目报错

    IntelliJ IDEA编译项目报错 "xxx包不存在" 或 "找不到符号"

    这篇文章主要介绍了IntelliJ IDEA编译项目报错 "xxx包不存在" 或 "找不到符号" ,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • java程序设计语言的优势及特点

    java程序设计语言的优势及特点

    在本篇文章里小编给大家分享的是一篇关于java程序设计语言的优势及特点的内容,需要的朋友们可以学习参考下。
    2020-02-02
  • Java中本地缓存的4种实现方式总结

    Java中本地缓存的4种实现方式总结

    这篇文章主要介绍了Java中本地缓存的4种实现方式,分别是基础缓存实现、GuavaLoadingCache、SpringBoot整合Caffeine和JetCache,通过实例代码,详细讲解了每种缓存技术的特点和使用方法,需要的朋友可以参考下
    2025-04-04
  • Log4j按级别输出日志到不同文件的实现方法

    Log4j按级别输出日志到不同文件的实现方法

    下面小编就为大家带来一篇Log4j按级别输出日志到不同文件的实现方法。
    2016-11-11
  • 通过代码示例了解submit与execute的区别

    通过代码示例了解submit与execute的区别

    这篇文章主要介绍了通过代码示例了解submit与execute的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • springboot如何使用redis的incr创建分布式自增id

    springboot如何使用redis的incr创建分布式自增id

    这篇文章主要介绍了springboot如何使用redis的incr创建分布式自增id,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 解决异常FileNotFoundException:class path resource找不到资源文件的问题

    解决异常FileNotFoundException:class path resource找不到资源文件的问题

    今天小编就为大家分享一篇关于解决异常FileNotFoundException:class path resource找不到资源文件的问题,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12

最新评论