掌握AbstractQueuedSynchronizer的独门秘籍
2023-12-22 01:30:48
独占锁的魅力
在并发编程中,独占锁是一种重要的同步机制,它确保同一时刻只有一个线程能够访问共享资源。AQS 提供了两种独占锁实现:ReentrantLock 和 StampedLock。ReentrantLock 是一个可重入锁,这意味着同一个线程可以多次获取同一个锁。而 StampedLock 则是一种乐观锁,它允许线程在不持有锁的情况下尝试获取共享资源。
揭开AQS的奥秘
AQS 的核心是 state 字段,它是一个 int 类型的值,用于表示锁的状态。state 字段的值可以是 0、1 或大于 1。当 state 字段的值为 0 时,表示锁处于未锁定状态;当 state 字段的值为 1 时,表示锁处于锁定状态;当 state 字段的值大于 1 时,表示锁处于等待状态。
ReentrantLock的精髓
ReentrantLock 是 AQS 提供的一个可重入锁实现。可重入锁允许同一个线程多次获取同一个锁,这对于构建递归算法或防止死锁非常有用。ReentrantLock 的实现基于 AQS 的 state 字段。当一个线程第一次获取 ReentrantLock 时,它会将 state 字段的值从 0 增加到 1,表示锁已锁定。当同一个线程再次获取 ReentrantLock 时,它会将 state 字段的值增加 1,表示锁的重入次数增加。当一个线程释放 ReentrantLock 时,它会将 state 字段的值减少 1,表示锁的重入次数减少。当 state 字段的值变为 0 时,表示锁已完全释放。
StampedLock的妙处
StampedLock 是 AQS 提供的另一个独占锁实现。StampedLock 是一种乐观锁,它允许线程在不持有锁的情况下尝试获取共享资源。StampedLock 的实现也基于 AQS 的 state 字段。当一个线程尝试获取 StampedLock 时,它会将 state 字段的值增加 1,表示锁已锁定。如果另一个线程也尝试获取 StampedLock,它会发现 state 字段的值大于 1,表示锁已锁定,然后它会进入等待队列。当持有锁的线程释放 StampedLock 时,它会将 state 字段的值减少 1,表示锁已释放,然后唤醒等待队列中的线程。
实例探究
为了更好地理解 AQS 的独占锁,让我们来看几个示例。
ReentrantLock示例
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 1 acquired the lock.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
Thread thread2 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 2 acquired the lock.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
thread1.start();
thread2.start();
}
}
在这个示例中,我们创建了一个 ReentrantLock 对象并将其命名为 lock。然后,我们创建了两个线程 thread1 和 thread2。每个线程都尝试获取 lock 并执行一些操作。由于 lock 是一个可重入锁,因此 thread1 可以多次获取 lock。
StampedLock示例
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private static StampedLock lock = new StampedLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
long stamp = lock.tryWriteLock();
try {
System.out.println("Thread 1 acquired the lock.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlockWrite(stamp);
}
});
Thread thread2 = new Thread(() -> {
long stamp = lock.tryReadLock();
try {
System.out.println("Thread 2 acquired the lock.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlockRead(stamp);
}
});
thread1.start();
thread2.start();
}
}
在这个示例中,我们创建了一个 StampedLock 对象并将其命名为 lock。然后,我们创建了两个线程 thread1 和 thread2。thread1 尝试获取 StampedLock 的写锁,而 thread2 尝试获取 StampedLock 的读锁。由于 StampedLock 是一种乐观锁,因此 thread1 和 thread2 可以同时获取 StampedLock。
结语
AbstractQueuedSynchronizer (AQS) 是 Java 并发编程中的一个重要组件,它提供了构建同步器(如锁和队列)的基础。AQS 的独占锁包括 ReentrantLock 和 StampedLock。ReentrantLock 是一个可重入锁,它允许同一个线程多次获取同一个锁。StampedLock 是一种乐观锁,它允许线程在不持有锁的情况下尝试获取共享资源。通过理解和应用 AQS 的独占锁,您可以构建出高效可靠的并发应用程序。