返回

AQS源代码解析:独占式获取锁的细节

后端

在并发编程领域,锁是至关重要的同步工具,它可以确保多个线程对共享资源的访问是互斥的,防止出现数据错乱和竞争条件。AQS(AbstractQueuedSynchronizer)是Java并发包中一个重要的锁实现,它提供了灵活且可扩展的锁实现框架。本文将深入剖析AQS源码,详细讲解独占式获取锁的细节,包括响应中断和超时获取锁。

独占式获取锁响应中断

AQS提供了acquire(int arg)方法以供独占式获取同步状态,但是该方法对中断不响应,对线程进行中断操作后,该方法不会抛出InterruptedException异常,而是继续尝试获取锁。为了支持对中断的响应,AQS提供了acquireInterruptibly(int arg)方法,该方法在等待获取锁的过程中,如果线程被中断,则抛出InterruptedException异常,从而使线程可以及时响应中断。

acquireInterruptibly(int arg)方法的实现如下:

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    acquire(arg);
}

可以看到,acquireInterruptibly(int arg)方法只是调用了acquire(int arg)方法,并没有做额外的处理。但是,在acquire(int arg)方法中,有一个检查中断的逻辑:

if (Thread.interrupted())
    throw new InterruptedException();

因此,acquireInterruptibly(int arg)方法实际上是在acquire(int arg)方法的基础上添加了对中断的检查,如果线程在等待获取锁的过程中被中断,则抛出InterruptedException异常。

独占式超时获取锁

在某些情况下,线程可能需要在指定的时间内获取锁,如果在指定的时间内无法获取锁,则放弃获取锁并继续执行。为了支持超时获取锁,AQS提供了tryAcquire(int arg, long timeout, TimeUnit unit)方法,该方法在等待获取锁的过程中,如果在指定的时间内无法获取锁,则返回false,否则返回true。

tryAcquire(int arg, long timeout, TimeUnit unit)方法的实现如下:

public final boolean tryAcquire(int arg, long timeout, TimeUnit unit)
        throws InterruptedException {
    long nanosTimeout = unit.toNanos(timeout);
    final ReentrantLockState state = state;
    if (state.isHeldExclusively())
        return false;
    int w = state.waiter;
    if (w == 0) {
        if (!state.compareAndSetState(0, 1))
            return false;
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
    }
    long deadline = System.nanoTime() + nanosTimeout;
    threadShouldBlock(); // Overrideable method
    while (true) {
        Thread next = state.next;
        if (next == null) { // Clean up if empty
            state.acquireQueued(this, arg);
        } else {
            state.prev = next.next;
        }
        boolean interrupted = Thread.interrupted();
        if (interrupted && state.next != this)
            throw new InterruptedException();
        if (System.nanoTime() > deadline &&
            state.next == this) {
            cancelAcquire(this);
            return false;
        }
        parkAndCheckInterrupt();
        if (w != state.waiter) {
            return false;
        } else if (state.isHeldExclusively()) {
            cancelAcquire(this);
            return true;
        }
    }
}

可以看到,tryAcquire(int arg, long timeout, TimeUnit unit)方法首先计算出等待超时的时间点,然后进入循环,在循环中,该方法首先检查锁的状态,如果锁已经被独占,则返回false;如果锁没有被独占,则尝试获取锁,如果获取成功,则返回true;如果获取失败,则将当前线程加入到等待队列中,并检查是否超时。如果超时,则从等待队列中删除当前线程,并返回false;如果未超时,则继续等待。

总结

AQS提供了丰富的锁获取方法,包括响应中断的独占式获取锁方法acquireInterruptibly(int arg)和超时获取锁方法tryAcquire(int arg, long timeout, TimeUnit unit)。这些方法可以满足不同场景下的锁获取需求,帮助开发者编写出高效且可靠的多线程程序。