AQS源代码解析:独占式获取锁的细节
2023-12-14 13:47:58
在并发编程领域,锁是至关重要的同步工具,它可以确保多个线程对共享资源的访问是互斥的,防止出现数据错乱和竞争条件。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)。这些方法可以满足不同场景下的锁获取需求,帮助开发者编写出高效且可靠的多线程程序。