Bootstrap

如何基于AQS实现一个自定义锁

如何基于AQS实现一个自定义锁

AQS是什么

AQS全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架

AbstractQueuedSynchronizer的重要属性有

private volatile int state;
private transient volatile Node head;
private transient volatile Node tail;

可以看到AbstractQueuedSynchronizer的内部有一个头节点Node和一个尾节点Node

那么Node节点是什么呢,是AbstractQueuedSynchronizer的一个内部类

volatile Thread thread;
volatile Node prev;
volatile Node next;
volatile int waitStatus;
Node nextWaiter;

可以看到Node节点是一个双向链表,除此之外还维护了一个thread线程和waitStatus变量以及nextWaiter节点

  1. thread: 这个变量保存了当前 Node 对象所代表的线程。当一个线程尝试获取锁但未能立即获得时,它会被封装在一个 Node 对象中,并将其添加到同步队列中。

  2. waitStatus: 这个整型变量用于表示节点在队列中的等待状态。它有几种可能的值:

    • CANCELLED: 表示线程已经取消了对同步状态的请求。

    • SIGNAL: 表示线程需要被唤醒(通常是因为其他线程释放了同步状态)。

    • CONDITION: 表示线程正在等待某个条件。

    • PROPAGATE: 表示下一次共享状态的释放应该传播到其他线程。

    • 0: 初始状态,表示节点没有特定的状态。

    waitStatus 值的变化是通过原子操作来保证线程安全。

  3. nextWaiter: 这个变量指向链表中的下一个 Node 对象,AQS 可以通过这个链表来管理等待队列中的线程。

那么AbstractQueuedSynchronizer内部有哪些方法呢

  1. acquire(int arg): 尝试获取一个单元的同步状态。如果获取成功,返回 true;如果失败,则将线程添加到等待队列中,并阻塞当前线程,直到再次尝试获取成功。

  2. acquireInterruptibly(int arg): 类似于 acquire,但线程可以被中断。

  3. tryAcquire(int arg): 尝试非阻塞地获取同步状态。默认实现总是返回 false,需要子类重写以实现特定的获取行为。

  4. tryRelease(int arg): 尝试释放一个单元的同步状态。默认实现总是返回 false,需要子类重写以实现特定的释放行为。

  5. acquireShared(int arg): 用于实现共享模式的同步器,如 ReadWriteLock

  6. acquireSharedInterruptibly(int arg): 与 acquireShared 类似,但线程可以被中断。

  7. tryAcquireShared(int arg): 尝试非阻塞地获取共享模式的同步状态。

  8. tryReleaseShared(int arg)尝试释放共享模式的同步状态。

  9. release(int arg): 释放同步状态。如果所有线程都被唤醒,这个方法将返回 true。

  10. releaseShared(int arg): 用于共享模式的同步器的释放操作。

  11. hasQueuedThreads(): 返回是否有线程正在等待获取同步状态。

  12. hasContended(): 返回是否有线程曾经争用过这个同步器。

  13. getFirstQueuedThread(): 返回等待队列中的第一个线程,如果没有线程等待,则返回 null。

  14. getQueuedThreads(): 返回一个包含所有等待线程的集合。

  15. getExclusiveQueuedThreads(): 返回一个包含所有等待获取独占访问权的线程的集合。

  16. getQueueLength(): 返回等待队列中的线程数。

  17. isHeldExclusively()默认实现中,它检查同步状态是否为零,如果为零,则表示当前没有线程持有独占锁

子类可以重写的方法主要有以下方法

tryAcquire 
tryRelease 
tryAcquireShared 
tryReleaseShared 
isHeldExclusively

实现一个同步器类

final class MySync extends AbstractQueuedSynchronizer {
    
    @Override
    protected boolean tryAcquire(int acquires) {
        if (acquires == 1){
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
        }
        return false;
    }
    
    @Override
    protected boolean tryRelease(int acquires) {
        if(acquires == 1) {
            if(getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        return false;
    }
    
    protected Condition newCondition() {
        return new ConditionObject();
    }
    
    @Override
    protected boolean isHeldExclusively() {
        return getState() == 1;
    }
    
}

自定义同步器类继承自AbstractQueuedSynchronizer,这里重写了tryAcquire()、tryRelease()和isHeldExclusively()方法,并提供了一个newCondition()方法来获得条件变量,ConditionObject是AbstractQueuedSynchronizer的一个内部类,用以实现条件变量

这个自定义同步器实现了一个基本的锁机制,其中:

  • 一个线程可以通过 tryAcquire 方法尝试获取锁。

  • 持有锁的线程可以通过 tryRelease 方法释放锁。

  • 线程可以使用 Condition 对象来等待或等待直到某个条件成立。

实现一个自定义锁

有了自定义同步器 ,就能更轻易实现一个功能完备的自定义锁

实现一个锁,首先要实现Lock接口,并实现其中的抽象方法,在其中定义一个自定义同步器成员变量

class MyLock implements Lock {
    
    static MySync sync = new MySync();
    
    @Override
    // 尝试阻塞获取锁,不成功则会进入等待队列
    public void lock() {
        sync.acquire(1);
    }
    
    @Override
    // 尝试阻塞获取锁,不成功则会进入等待队列,可打断
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    
    @Override
    // 尝试立即获取锁,不成功直接返回,不进入等待队列
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }
    
    @Override
    // 尝试阻塞获取锁,不成功则会进入等待队列,有超时时间
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }
    
    @Override
    // 释放锁
    public void unlock() {
        sync.release(1);
    }
    
    @Override
    // 生成条件变量
    public Condition newCondition() {
        return sync.newCondition();
    }
    
}
  1. lock(): 此方法调用 MySync 类的 acquire(1) 方法,尝试获取锁。如果锁已被其他线程持有,则当前线程将被放入等待队列中,直到能够获取锁。

  2. lockInterruptibly(): 此方法调用 MySync 类的 acquireInterruptibly(1) 方法,尝试获取锁。与 lock() 方法不同,如果线程在获取锁时被中断,将会抛出 InterruptedException

  3. tryLock(): 此方法调用 MySync 类的 tryAcquire(1) 方法,尝试立即获取锁,如果成功则返回 true,如果失败则返回 false,不会阻塞或进入等待队列。

  4. tryLock(long time, TimeUnit unit): 此方法调用 MySync 类的 tryAcquireNanos(1, unit.toNanos(time)) 方法,尝试在给定的等待时间内获取锁。如果超时,则返回 false。如果线程在尝试期间被中断,将抛出 InterruptedException

  5. unlock(): 此方法调用 MySync 类的 release(1) 方法,释放当前线程持有的锁。

  6. newCondition(): 此方法调用 MySync 类的 newCondition() 方法,创建一个新的 Condition 对象,允许线程在锁上等待或等待直到某个条件成立。

总结

本文先详细讲解了AQS,随后基于AQS实现了一个基本的不可重入锁

;