Java利用AQS实现自定义锁

2022-11-13 13:11:01 java 自定义 利用

什么是AQS

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

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

AQS原理

AQS 核心思想:

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

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

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

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

   
    private volatile int state;

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

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

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

 源代码如下

    
    protected final int getState() {
        return state;
    }
 
    
    protected final void setState(int newState) {
        state = newState;
    }
    
    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实现自定义锁内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章